diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 6f2e14376b..1b41033557 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -37,7 +37,7 @@ runs: sudo rsync -a --delete /tmp/empty/ /usr/local/lib/android/ - name: Install uv if: ${{ contains(inputs.dependencies, 'python') }} - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v5 + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v5 with: enable-cache: true - name: Setup python @@ -64,12 +64,12 @@ runs: rustflags: "" - name: Setup rust dependencies if: ${{ contains(inputs.dependencies, 'rust') }} - uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2 + uses: taiki-e/install-action@055f5df8c3f65ea01cd41e9dc855becd88953486 # v2 with: tool: cargo-deny cargo-machete cargo-llvm-cov nextest - name: Setup node (web) if: ${{ contains(inputs.dependencies, 'node') }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4 with: node-version-file: "${{ inputs.working-directory }}web/package.json" cache: "npm" @@ -77,7 +77,7 @@ runs: registry-url: "https://registry.npmjs.org" - name: Setup node (root) if: ${{ contains(inputs.dependencies, 'node') }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4 with: node-version-file: "${{ inputs.working-directory }}package.json" cache: "npm" diff --git a/.github/actions/setup/compose.yml b/.github/actions/setup/compose.yml index fc2f65e75b..2fda7a13ac 100644 --- a/.github/actions/setup/compose.yml +++ b/.github/actions/setup/compose.yml @@ -2,7 +2,7 @@ services: postgresql: image: docker.io/library/postgres:${PSQL_TAG:-16} volumes: - - db-data:/var/lib/postgresql/data + - db-data:/var/lib/postgresql command: "-c log_statement=all" environment: POSTGRES_USER: authentik diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3eb67c1848..e638882627 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -66,6 +66,8 @@ updates: default-days: 7 semver-major-days: 14 semver-patch-days: 3 + exclude: + - rustls-webpki - package-ecosystem: rust-toolchain directory: "/" diff --git a/.github/workflows/_reusable-docker-build.yml b/.github/workflows/_reusable-docker-build.yml index 01d9e9a3f9..a601048e95 100644 --- a/.github/workflows/_reusable-docker-build.yml +++ b/.github/workflows/_reusable-docker-build.yml @@ -90,7 +90,7 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: int128/docker-manifest-create-action@44422a4b046d55dc036df622039ed3aec43c613c # v2 + - uses: int128/docker-manifest-create-action@3de37de96c4e900bc3eef9055d3c50abdb4f769d # v2 id: build with: tags: ${{ matrix.tag }} diff --git a/.github/workflows/ci-api-docs.yml b/.github/workflows/ci-api-docs.yml index 3bb61e02db..a0bc8e002e 100644 --- a/.github/workflows/ci-api-docs.yml +++ b/.github/workflows/ci-api-docs.yml @@ -33,7 +33,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: website/package.json cache: "npm" @@ -71,7 +71,7 @@ jobs: with: name: api-docs path: website/api/build - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: website/package.json cache: "npm" diff --git a/.github/workflows/ci-aws-cfn.yml b/.github/workflows/ci-aws-cfn.yml index 5d8c655908..541e6915c6 100644 --- a/.github/workflows/ci-aws-cfn.yml +++ b/.github/workflows/ci-aws-cfn.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - name: Setup authentik env uses: ./.github/actions/setup - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: lifecycle/aws/package.json cache: "npm" diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index 51552828e1..c57a1456ba 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -36,7 +36,7 @@ jobs: NODE_ENV: production steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: website/package.json cache: "npm" @@ -53,7 +53,7 @@ jobs: NODE_ENV: production steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: website/package.json cache: "npm" diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 2a6dce2c85..a6627949ee 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -127,7 +127,10 @@ jobs: with: postgresql_version: ${{ matrix.psql }} - name: run migrations to stable - run: uv run python -m lifecycle.migrate + run: | + docker ps + docker logs setup-postgresql-1 + uv run python -m lifecycle.migrate - name: checkout current code run: | set -x diff --git a/.github/workflows/ci-outpost.yml b/.github/workflows/ci-outpost.yml index 5f478a7fc8..4a6569f088 100644 --- a/.github/workflows/ci-outpost.yml +++ b/.github/workflows/ci-outpost.yml @@ -145,7 +145,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: "go.mod" - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: web/package.json cache: "npm" diff --git a/.github/workflows/ci-web.yml b/.github/workflows/ci-web.yml index 0784b03a84..1d7a64c44b 100644 --- a/.github/workflows/ci-web.yml +++ b/.github/workflows/ci-web.yml @@ -32,7 +32,7 @@ jobs: project: web steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: ${{ matrix.project }}/package.json cache: "npm" @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: web/package.json cache: "npm" @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: web/package.json cache: "npm" diff --git a/.github/workflows/packages-npm-publish.yml b/.github/workflows/packages-npm-publish.yml index 96dec932e4..6dee855434 100644 --- a/.github/workflows/packages-npm-publish.yml +++ b/.github/workflows/packages-npm-publish.yml @@ -35,13 +35,13 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 with: fetch-depth: 2 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: ${{ matrix.package }}/package.json registry-url: "https://registry.npmjs.org" - name: Get changed files id: changed-files - uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62 + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62 with: files: | ${{ matrix.package }}/package.json diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 76abd0768d..2434842388 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -87,7 +87,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: "go.mod" - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: web/package.json cache: "npm" @@ -151,7 +151,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: "go.mod" - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5 with: node-version-file: web/package.json cache: "npm" diff --git a/Cargo.lock b/Cargo.lock index e663e346e8..87d5535641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,7 +208,7 @@ dependencies = [ "time", "tokio", "tokio-retry2", - "tokio-tungstenite 0.29.0", + "tokio-tungstenite", "tracing", "url", "uuid", @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", "axum-macros", @@ -359,7 +359,7 @@ dependencies = [ "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite 0.28.0", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", @@ -387,9 +387,9 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" dependencies = [ "proc-macro2", "quote", @@ -597,9 +597,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -619,9 +619,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -2176,7 +2176,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -2776,9 +2776,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -3046,9 +3046,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" dependencies = [ "aws-lc-rs", "log", @@ -3111,9 +3111,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -3630,7 +3630,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "sha1", @@ -3671,7 +3671,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "sha2", @@ -3919,9 +3919,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -3977,18 +3977,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.28.0", -] - [[package]] name = "tokio-tungstenite" version = "0.29.0" @@ -4001,7 +3989,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.29.0", + "tungstenite", "webpki-roots 0.26.11", ] @@ -4204,23 +4192,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.2", - "sha1", - "thiserror 2.0.18", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.29.0" @@ -4347,12 +4318,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8-zero" version = "0.8.1" @@ -4373,9 +4338,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index ec355a41af..2134ffbeba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,9 @@ publish = false arc-swap = "= 1.9.1" argh = "= 0.1.19" axum-server = { version = "= 0.8.0", features = ["tls-rustls-no-provider"] } -aws-lc-rs = { version = "= 1.16.2", features = ["fips"] } -axum = { version = "= 0.8.8", features = ["http2", "macros", "ws"] } -clap = { version = "= 4.6.0", features = ["derive", "env"] } +aws-lc-rs = { version = "= 1.16.3", features = ["fips"] } +axum = { version = "= 0.8.9", features = ["http2", "macros", "ws"] } +clap = { version = "= 4.6.1", features = ["derive", "env"] } client-ip = { version = "0.2.1", features = ["forwarded-header"] } color-eyre = "= 0.6.5" colored = "= 3.1.1" @@ -66,7 +66,7 @@ reqwest-middleware = { version = "= 0.5.1", features = [ "query", "rustls", ] } -rustls = { version = "= 0.23.37", features = ["fips"] } +rustls = { version = "= 0.23.38", features = ["fips"] } sentry = { version = "= 0.47.0", default-features = false, features = [ "backtrace", "contexts", @@ -97,7 +97,7 @@ sqlx = { version = "= 0.8.6", default-features = false, features = [ tempfile = "= 3.27.0" thiserror = "= 2.0.18" time = { version = "= 0.3.47", features = ["macros"] } -tokio = { version = "= 1.51.1", features = ["full", "tracing"] } +tokio = { version = "= 1.52.1", features = ["full", "tracing"] } tokio-retry2 = "= 0.9.1" tokio-rustls = "= 0.26.4" tokio-tungstenite = { version = "= 0.29.0", features = [ @@ -116,7 +116,7 @@ tracing-subscriber = { version = "= 0.3.23", features = [ "tracing-log", ] } url = "= 2.5.8" -uuid = { version = "= 1.23.0", features = ["serde", "v4"] } +uuid = { version = "= 1.23.1", features = ["serde", "v4"] } ak-axum = { package = "authentik-axum", version = "2026.5.0-rc1", path = "./packages/ak-axum" } ak-client = { package = "authentik-client", version = "2026.5.0-rc1", path = "./packages/client-rust" } diff --git a/authentik/admin/files/backends/s3.py b/authentik/admin/files/backends/s3.py index f50f43b073..2a867a3a58 100644 --- a/authentik/admin/files/backends/s3.py +++ b/authentik/admin/files/backends/s3.py @@ -1,7 +1,7 @@ from collections.abc import Generator, Iterator from contextlib import contextmanager from tempfile import SpooledTemporaryFile -from urllib.parse import urlsplit +from urllib.parse import urlsplit, urlunsplit import boto3 from botocore.config import Config @@ -164,16 +164,19 @@ class S3Backend(ManageableBackend): ) def _file_url(name: str, request: HttpRequest | None) -> str: + client = self.client params = { "Bucket": self.bucket_name, "Key": f"{self.base_path}/{name}", } - url = self.client.generate_presigned_url( - "get_object", - Params=params, - ExpiresIn=expires_in, - HttpMethod="GET", + operation_name = "GetObject" + operation_model = client.meta.service_model.operation_model(operation_name) + request_dict = client._convert_to_request_dict( + params, + operation_model, + endpoint_url=client.meta.endpoint_url, + context={"is_presign_request": True}, ) # Support custom domain for S3-compatible storage (so not AWS) @@ -183,9 +186,8 @@ class S3Backend(ManageableBackend): CONFIG.get(f"storage.{self.name}.custom_domain", None), ) if custom_domain: - parsed = urlsplit(url) scheme = "https" if use_https else "http" - path = parsed.path + path = request_dict["url_path"] # When using path-style addressing, the presigned URL contains the bucket # name in the path (e.g., /bucket-name/key). Since custom_domain must @@ -200,9 +202,22 @@ class S3Backend(ManageableBackend): if not path.startswith("/"): path = f"/{path}" - url = f"{scheme}://{custom_domain}{path}?{parsed.query}" + custom_base = urlsplit(f"{scheme}://{custom_domain}") - return url + # Sign the final public URL instead of signing the internal S3 endpoint and + # rewriting it afterwards. Presigned SigV4 URLs include the host header in the + # canonical request, so post-sign host changes break strict backends like RustFS. + public_path = f"{custom_base.path.rstrip('/')}{path}" if custom_base.path else path + request_dict["url_path"] = public_path + request_dict["url"] = urlunsplit( + (custom_base.scheme, custom_base.netloc, public_path, "", "") + ) + + return client._request_signer.generate_presigned_url( + request_dict, + operation_name, + expires_in=expires_in, + ) if use_cache: return self._cache_get_or_set(name, request, _file_url, expires_in) diff --git a/authentik/admin/files/backends/tests/test_s3_backend.py b/authentik/admin/files/backends/tests/test_s3_backend.py index 769d886f40..23aa8ddfb9 100644 --- a/authentik/admin/files/backends/tests/test_s3_backend.py +++ b/authentik/admin/files/backends/tests/test_s3_backend.py @@ -1,4 +1,5 @@ from unittest import skipUnless +from urllib.parse import parse_qs, urlsplit from botocore.exceptions import UnsupportedSignatureVersionError from django.test import TestCase @@ -168,6 +169,44 @@ class TestS3Backend(FileTestS3BackendMixin, TestCase): f"URL: {url}", ) + @CONFIG.patch("storage.s3.secure_urls", False) + @CONFIG.patch("storage.s3.addressing_style", "path") + def test_file_url_custom_domain_resigns_for_custom_host(self): + """Test presigned URLs are signed for the custom domain host. + + Host-changing custom domains must produce a signature query string for + the public host, not reuse the internal endpoint signature. + """ + bucket_name = self.media_s3_bucket_name + key_name = "application-icons/test.svg" + custom_domain = f"files.example.test:8020/{bucket_name}" + + endpoint_signed_url = self.media_s3_backend.client.generate_presigned_url( + "get_object", + Params={ + "Bucket": bucket_name, + "Key": f"{self.media_s3_backend.base_path}/{key_name}", + }, + ExpiresIn=900, + HttpMethod="GET", + ) + + with CONFIG.patch("storage.media.s3.custom_domain", custom_domain): + custom_url = self.media_s3_backend.file_url(key_name, use_cache=False) + + endpoint_parts = urlsplit(endpoint_signed_url) + custom_parts = urlsplit(custom_url) + + self.assertEqual(custom_parts.scheme, "http") + self.assertEqual(custom_parts.netloc, "files.example.test:8020") + self.assertEqual(parse_qs(custom_parts.query)["X-Amz-SignedHeaders"], ["host"]) + self.assertNotEqual( + custom_parts.query, + endpoint_parts.query, + "Custom-domain URLs must be signed for the public host, not reuse the endpoint " + "signature query string.", + ) + def test_themed_urls_without_theme_variable(self): """Test themed_urls returns None when filename has no %(theme)s""" result = self.media_s3_backend.themed_urls("logo.png") diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index e963f94568..846b88886a 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -4,7 +4,7 @@ from collections.abc import Iterator from copy import copy from django.core.cache import cache -from django.db.models import Case, QuerySet +from django.db.models import Case, Q, QuerySet from django.db.models.expressions import When from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ @@ -36,9 +36,13 @@ from authentik.rbac.filters import ObjectFilter LOGGER = get_logger() -def user_app_cache_key(user_pk: str, page_number: int | None = None) -> str: +def user_app_cache_key( + user_pk: str, page_number: int | None = None, only_with_launch_url: bool = False +) -> str: """Cache key where application list for user is saved""" key = f"{CACHE_PREFIX}app_access/{user_pk}" + if only_with_launch_url: + key += "/launch" if page_number: key += f"/{page_number}" return key @@ -274,11 +278,19 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): if superuser_full_list and request.user.is_superuser: return super().list(request) - only_with_launch_url = str( - request.query_params.get("only_with_launch_url", "false") - ).lower() + only_with_launch_url = ( + str(request.query_params.get("only_with_launch_url", "false")).lower() + ) == "true" queryset = self._filter_queryset_for_list(self.get_queryset()) + if only_with_launch_url: + # Pre-filter at DB level to skip expensive per-app policy evaluation + # for apps that can never appear in the launcher: + # - No meta_launch_url AND no provider: no possible launch URL + # - meta_launch_url="blank://blank": documented convention to hide from launcher + queryset = queryset.exclude( + Q(meta_launch_url="", provider__isnull=True) | Q(meta_launch_url="blank://blank") + ) paginator: Pagination = self.paginator paginated_apps = paginator.paginate_queryset(queryset, request) @@ -295,7 +307,6 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): except ValueError as exc: raise ValidationError from exc allowed_applications = self._get_allowed_applications(paginated_apps, user=for_user) - allowed_applications = self._expand_applications(allowed_applications) serializer = self.get_serializer(allowed_applications, many=True) return self.get_paginated_response(serializer.data) @@ -305,19 +316,26 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): allowed_applications = self._get_allowed_applications(paginated_apps) if should_cache: allowed_applications = cache.get( - user_app_cache_key(self.request.user.pk, paginator.page.number) + user_app_cache_key( + self.request.user.pk, paginator.page.number, only_with_launch_url + ) ) - if not allowed_applications: + if allowed_applications: + # Re-fetch cached applications since pickled instances lose prefetched + # relationships, causing N+1 queries during serialization + allowed_applications = self._expand_applications(allowed_applications) + else: LOGGER.debug("Caching allowed application list", page=paginator.page.number) allowed_applications = self._get_allowed_applications(paginated_apps) cache.set( - user_app_cache_key(self.request.user.pk, paginator.page.number), + user_app_cache_key( + self.request.user.pk, paginator.page.number, only_with_launch_url + ), allowed_applications, timeout=86400, ) - allowed_applications = self._expand_applications(allowed_applications) - if only_with_launch_url == "true": + if only_with_launch_url: allowed_applications = self._filter_applications_with_launch_url(allowed_applications) serializer = self.get_serializer(allowed_applications, many=True) diff --git a/authentik/core/models.py b/authentik/core/models.py index 4043baa9b8..be8421a6d2 100644 --- a/authentik/core/models.py +++ b/authentik/core/models.py @@ -790,9 +790,13 @@ class Application(SerializerModel, PolicyBindingModel): def get_provider(self) -> Provider | None: """Get casted provider instance. Needs Application queryset with_provider""" + if hasattr(self, "_cached_provider"): + return self._cached_provider if not self.provider: + self._cached_provider = None return None - return get_deepest_child(self.provider) + self._cached_provider = get_deepest_child(self.provider) + return self._cached_provider def backchannel_provider_for[T: Provider](self, provider_type: type[T], **kwargs) -> T | None: """Get Backchannel provider for a specific type""" diff --git a/authentik/endpoints/connectors/agent/controller.py b/authentik/endpoints/connectors/agent/controller.py index 4dc698db64..ef144e522c 100644 --- a/authentik/endpoints/connectors/agent/controller.py +++ b/authentik/endpoints/connectors/agent/controller.py @@ -138,13 +138,7 @@ class AgentConnectorController(BaseController[AgentConnector]): "AllowDeviceIdentifiersInAttestation": True, "AuthenticationMethod": "UserSecureEnclaveKey", "EnableAuthorization": True, - "EnableCreateUserAtLogin": True, - "FileVaultPolicy": ["RequireAuthentication"], - "LoginPolicy": ["RequireAuthentication"], - "NewUserAuthorizationMode": "Standard", - "UnlockPolicy": ["RequireAuthentication"], "UseSharedDeviceKeys": True, - "UserAuthorizationMode": "Standard", }, }, ], diff --git a/authentik/endpoints/connectors/agent/migrations/0005_appleindependentsecureenclave.py b/authentik/endpoints/connectors/agent/migrations/0005_appleindependentsecureenclave.py new file mode 100644 index 0000000000..4df761805f --- /dev/null +++ b/authentik/endpoints/connectors/agent/migrations/0005_appleindependentsecureenclave.py @@ -0,0 +1,54 @@ +# Generated by Django 5.2.12 on 2026-03-06 14:38 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "authentik_endpoints_connectors_agent", + "0004_agentconnector_challenge_idle_timeout_and_more", + ), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="AppleIndependentSecureEnclave", + fields=[ + ("created", models.DateTimeField(auto_now_add=True)), + ("last_updated", models.DateTimeField(auto_now=True)), + ( + "name", + models.CharField( + help_text="The human-readable name of this device.", max_length=64 + ), + ), + ( + "confirmed", + models.BooleanField(default=True, help_text="Is this device ready for use?"), + ), + ("last_used", models.DateTimeField(null=True)), + ("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ("apple_secure_enclave_key", models.TextField()), + ("apple_enclave_key_id", models.TextField()), + ("device_type", models.TextField()), + ( + "user", + models.ForeignKey( + help_text="The user that this device belongs to.", + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Apple Independent Secure Enclave", + "verbose_name_plural": "Apple Independent Secure Enclaves", + }, + ), + ] diff --git a/authentik/endpoints/connectors/agent/models.py b/authentik/endpoints/connectors/agent/models.py index 6a2d81c228..95be2dd055 100644 --- a/authentik/endpoints/connectors/agent/models.py +++ b/authentik/endpoints/connectors/agent/models.py @@ -19,6 +19,7 @@ from authentik.flows.stage import StageView from authentik.lib.generators import generate_key from authentik.lib.models import InternallyManagedMixin, SerializerModel from authentik.lib.utils.time import timedelta_string_validator +from authentik.stages.authenticator.models import Device as Authenticator if TYPE_CHECKING: from authentik.endpoints.connectors.agent.controller import AgentConnectorController @@ -172,3 +173,17 @@ class AppleNonce(InternallyManagedMixin, ExpiringModel): class Meta(ExpiringModel.Meta): verbose_name = _("Apple Nonce") verbose_name_plural = _("Apple Nonces") + + +class AppleIndependentSecureEnclave(Authenticator): + """A device-independent secure enclave key, used by Tap-to-login""" + + uuid = models.UUIDField(primary_key=True, default=uuid4) + + apple_secure_enclave_key = models.TextField() + apple_enclave_key_id = models.TextField() + device_type = models.TextField() + + class Meta: + verbose_name = _("Apple Independent Secure Enclave") + verbose_name_plural = _("Apple Independent Secure Enclaves") diff --git a/authentik/enterprise/endpoints/connectors/agent/api/secure_enclave.py b/authentik/enterprise/endpoints/connectors/agent/api/secure_enclave.py new file mode 100644 index 0000000000..b2149ea9c9 --- /dev/null +++ b/authentik/enterprise/endpoints/connectors/agent/api/secure_enclave.py @@ -0,0 +1,28 @@ +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.used_by import UsedByMixin +from authentik.core.api.utils import ModelSerializer +from authentik.endpoints.connectors.agent.models import AppleIndependentSecureEnclave + + +class AppleIndependentSecureEnclaveSerializer(ModelSerializer): + class Meta: + model = AppleIndependentSecureEnclave + fields = [ + "uuid", + "user", + "apple_secure_enclave_key", + "apple_enclave_key_id", + "device_type", + ] + + +class AppleIndependentSecureEnclaveViewSet(UsedByMixin, ModelViewSet): + queryset = AppleIndependentSecureEnclave.objects.all() + serializer_class = AppleIndependentSecureEnclaveSerializer + search_fields = [ + "name", + "user__name", + ] + ordering = ["uuid"] + filterset_fields = ["user", "apple_enclave_key_id"] diff --git a/authentik/enterprise/endpoints/connectors/agent/tests/test_apple_token.py b/authentik/enterprise/endpoints/connectors/agent/tests/test_apple_token.py index 724f57c4cf..3e708cb929 100644 --- a/authentik/enterprise/endpoints/connectors/agent/tests/test_apple_token.py +++ b/authentik/enterprise/endpoints/connectors/agent/tests/test_apple_token.py @@ -11,6 +11,7 @@ from authentik.endpoints.connectors.agent.models import ( AgentConnector, AgentDeviceConnection, AgentDeviceUserBinding, + AppleIndependentSecureEnclave, AppleNonce, DeviceToken, EnrollmentToken, @@ -25,7 +26,7 @@ class TestAppleToken(TestCase): def setUp(self): self.apple_sign_key = create_test_cert(PrivateKeyAlg.ECDSA) - sign_key_pem = self.apple_sign_key.public_key.public_bytes( + self.sign_key_pem = self.apple_sign_key.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode() @@ -50,7 +51,7 @@ class TestAppleToken(TestCase): device=self.device, connector=self.connector, apple_sign_key_id=self.apple_sign_key.kid, - apple_signing_key=sign_key_pem, + apple_signing_key=self.sign_key_pem, apple_encryption_key=self.enc_pub, ) self.user = create_test_user() @@ -59,7 +60,7 @@ class TestAppleToken(TestCase): user=self.user, order=0, apple_enclave_key_id=self.apple_sign_key.kid, - apple_secure_enclave_key=sign_key_pem, + apple_secure_enclave_key=self.sign_key_pem, ) self.device_token = DeviceToken.objects.create(device=self.connection) @@ -113,3 +114,62 @@ class TestAppleToken(TestCase): ).first() self.assertIsNotNone(event) self.assertEqual(event.context["device"]["name"], self.device.name) + + @reconcile_app("authentik_crypto") + def test_token_independent(self): + nonce = generate_id() + + AgentDeviceUserBinding.objects.all().delete() + AppleIndependentSecureEnclave.objects.create( + user=self.user, + apple_enclave_key_id=self.apple_sign_key.kid, + apple_secure_enclave_key=self.sign_key_pem, + ) + + AppleNonce.objects.create( + device_token=self.device_token, + nonce=nonce, + ) + embedded = encode( + {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce}, + self.apple_sign_key.private_key, + headers={ + "kid": self.apple_sign_key.kid, + }, + algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key), + ) + assertion = encode( + { + "iss": str(self.connector.pk), + "aud": "http://testserver/endpoints/agent/psso/token/", + "request_nonce": nonce, + "assertion": embedded, + "jwe_crypto": { + "apv": ( + "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE" + "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw" + ) + }, + }, + self.apple_sign_key.private_key, + headers={ + "kid": self.apple_sign_key.kid, + }, + algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key), + ) + res = self.client.post( + reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"), + data={ + "assertion": assertion, + "platform_sso_version": "1.0", + "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", + }, + ) + + self.assertEqual(res.status_code, 200) + event = Event.objects.filter( + action=EventAction.LOGIN, + app="authentik.endpoints.connectors.agent", + ).first() + self.assertIsNotNone(event) + self.assertEqual(event.context["device"]["name"], self.device.name) diff --git a/authentik/enterprise/endpoints/connectors/agent/urls.py b/authentik/enterprise/endpoints/connectors/agent/urls.py index 743586ff14..22a88a7f4d 100644 --- a/authentik/enterprise/endpoints/connectors/agent/urls.py +++ b/authentik/enterprise/endpoints/connectors/agent/urls.py @@ -1,5 +1,8 @@ from django.urls import path +from authentik.enterprise.endpoints.connectors.agent.api.secure_enclave import ( + AppleIndependentSecureEnclaveViewSet, +) from authentik.enterprise.endpoints.connectors.agent.views.apple_jwks import AppleJWKSView from authentik.enterprise.endpoints.connectors.agent.views.apple_nonce import NonceView from authentik.enterprise.endpoints.connectors.agent.views.apple_register import ( @@ -23,6 +26,7 @@ urlpatterns = [ ] api_urlpatterns = [ + ("endpoints/agents/psso/ise", AppleIndependentSecureEnclaveViewSet), path( "endpoints/agents/psso/register/device/", RegisterDeviceView.as_view(), diff --git a/authentik/enterprise/endpoints/connectors/agent/views/apple_token.py b/authentik/enterprise/endpoints/connectors/agent/views/apple_token.py index 0fba86ff47..61acfe4417 100644 --- a/authentik/enterprise/endpoints/connectors/agent/views/apple_token.py +++ b/authentik/enterprise/endpoints/connectors/agent/views/apple_token.py @@ -19,6 +19,7 @@ from authentik.endpoints.connectors.agent.models import ( AgentConnector, AgentDeviceConnection, AgentDeviceUserBinding, + AppleIndependentSecureEnclave, AppleNonce, DeviceAuthenticationToken, ) @@ -103,7 +104,9 @@ class TokenView(View): nonce.delete() return decoded - def validate_embedded_assertion(self, assertion: str) -> tuple[AgentDeviceUserBinding, dict]: + def validate_embedded_assertion( + self, assertion: str + ) -> tuple[AgentDeviceUserBinding | AppleIndependentSecureEnclave, dict]: """Decode an embedded assertion and validate it by looking up the matching device user""" decode_unvalidated = get_unverified_header(assertion) expected_kid = decode_unvalidated["kid"] @@ -112,8 +115,13 @@ class TokenView(View): target=self.device_connection.device, apple_enclave_key_id=expected_kid ).first() if not device_user: - LOGGER.warning("Could not find device user binding for user") - raise ValidationError("Invalid request") + independent_user = AppleIndependentSecureEnclave.objects.filter( + apple_enclave_key_id=expected_kid + ).first() + if not independent_user: + LOGGER.warning("Could not find device user binding or independent enclave for user") + raise ValidationError("Invalid request") + device_user = independent_user decoded: dict[str, Any] = decode( assertion, device_user.apple_secure_enclave_key, diff --git a/authentik/flows/exceptions.py b/authentik/flows/exceptions.py index 81b9793507..0c65f93052 100644 --- a/authentik/flows/exceptions.py +++ b/authentik/flows/exceptions.py @@ -11,6 +11,10 @@ class FlowNonApplicableException(SentryIgnoredException): policy_result: PolicyResult | None = None + def __init__(self, policy_result: PolicyResult | None = None, *args): + super().__init__(*args) + self.policy_result = policy_result + @property def messages(self) -> str: """Get messages from policy result, fallback to generic reason""" diff --git a/authentik/flows/migrations/0027_auto_20231028_1424.py b/authentik/flows/migrations/0027_auto_20231028_1424.py index a46aec0a35..466cf24593 100644 --- a/authentik/flows/migrations/0027_auto_20231028_1424.py +++ b/authentik/flows/migrations/0027_auto_20231028_1424.py @@ -42,6 +42,7 @@ class Migration(migrations.Migration): ("require_superuser", "Require Superuser"), ("require_redirect", "Require Redirect"), ("require_outpost", "Require Outpost"), + ("require_token", "Require Token"), ], default="none", help_text="Required level of authentication and authorization to access a flow.", diff --git a/authentik/flows/models.py b/authentik/flows/models.py index 1a5563792a..dfedaa6ed7 100644 --- a/authentik/flows/models.py +++ b/authentik/flows/models.py @@ -40,6 +40,7 @@ class FlowAuthenticationRequirement(models.TextChoices): REQUIRE_SUPERUSER = "require_superuser" REQUIRE_REDIRECT = "require_redirect" REQUIRE_OUTPOST = "require_outpost" + REQUIRE_TOKEN = "require_token" class NotConfiguredAction(models.TextChoices): diff --git a/authentik/flows/planner.py b/authentik/flows/planner.py index d81eff4d7d..61c2c13f40 100644 --- a/authentik/flows/planner.py +++ b/authentik/flows/planner.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any from django.core.cache import cache from django.http import HttpRequest, HttpResponse +from django.utils.translation import gettext as _ from sentry_sdk import start_span from sentry_sdk.tracing import Span from structlog.stdlib import BoundLogger, get_logger @@ -26,6 +27,7 @@ from authentik.lib.config import CONFIG from authentik.lib.utils.urls import redirect_with_qs from authentik.outposts.models import Outpost from authentik.policies.engine import PolicyEngine +from authentik.policies.types import PolicyResult from authentik.root.middleware import ClientIPMiddleware if TYPE_CHECKING: @@ -226,6 +228,15 @@ class FlowPlanner: and context.get(PLAN_CONTEXT_IS_REDIRECTED) is None ): raise FlowNonApplicableException() + if ( + self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_TOKEN + and context.get(PLAN_CONTEXT_IS_RESTORED) is None + ): + raise FlowNonApplicableException( + PolicyResult( + False, _("This link is invalid or has expired. Please request a new one.") + ) + ) outpost_user = ClientIPMiddleware.get_outpost_user(request) if self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_OUTPOST: if not outpost_user: @@ -273,9 +284,7 @@ class FlowPlanner: engine.build() result = engine.result if not result.passing: - exc = FlowNonApplicableException() - exc.policy_result = result - raise exc + raise FlowNonApplicableException(result) # User is passing so far, check if we have a cached plan cached_plan_key = cache_key(self.flow, user) cached_plan = cache.get(cached_plan_key, None) diff --git a/authentik/flows/tests/test_executor.py b/authentik/flows/tests/test_executor.py index 117e866758..28b1a03414 100644 --- a/authentik/flows/tests/test_executor.py +++ b/authentik/flows/tests/test_executor.py @@ -1,5 +1,6 @@ """flow views tests""" +from datetime import timedelta from unittest.mock import MagicMock, PropertyMock, patch from urllib.parse import urlencode @@ -7,6 +8,7 @@ from django.http import HttpRequest, HttpResponse from django.test import override_settings from django.test.client import RequestFactory from django.urls import reverse +from django.utils.timezone import now from rest_framework.exceptions import ParseError from authentik.core.models import Group, User @@ -17,6 +19,7 @@ from authentik.flows.models import ( FlowDeniedAction, FlowDesignation, FlowStageBinding, + FlowToken, InvalidResponseAction, ) from authentik.flows.planner import FlowPlan, FlowPlanner @@ -24,6 +27,7 @@ from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageVie from authentik.flows.tests import FlowTestCase from authentik.flows.views.executor import ( NEXT_ARG_NAME, + QS_KEY_TOKEN, QS_QUERY, SESSION_KEY_PLAN, FlowExecutorView, @@ -740,3 +744,77 @@ class TestFlowExecutor(FlowTestCase): "title": flow.title, }, ) + + @patch( + "authentik.flows.views.executor.to_stage_response", + TO_STAGE_RESPONSE_MOCK, + ) + def test_expired_flow_token(self): + """Test that an expired flow token shows an appropriate error message""" + flow = create_test_flow( + FlowDesignation.RECOVERY, + authentication=FlowAuthenticationRequirement.REQUIRE_TOKEN, + ) + user = create_test_user() + plan = FlowPlan(flow_pk=flow.pk.hex, bindings=[], markers=[]) + + token = FlowToken.objects.create( + user=user, + identifier=generate_id(), + flow=flow, + _plan=FlowToken.pickle(plan), + expires=now() - timedelta(hours=1), + ) + + url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}) + response = self.client.get( + url + f"?{urlencode({QS_QUERY: urlencode({QS_KEY_TOKEN: token.key})})}" + ) + self.assertStageResponse( + response, + flow, + component="ak-stage-access-denied", + error_message="This link is invalid or has expired. Please request a new one.", + ) + + @patch( + "authentik.flows.views.executor.to_stage_response", + TO_STAGE_RESPONSE_MOCK, + ) + def test_invalid_flow_token_require_token(self): + """Test that an invalid/nonexistent token on a REQUIRE_TOKEN flow shows error""" + flow = create_test_flow( + FlowDesignation.RECOVERY, + authentication=FlowAuthenticationRequirement.REQUIRE_TOKEN, + ) + + url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}) + response = self.client.get( + url + f"?{urlencode({QS_QUERY: urlencode({QS_KEY_TOKEN: 'invalid-token'})})}" + ) + self.assertStageResponse( + response, + flow, + component="ak-stage-access-denied", + error_message="This link is invalid or has expired. Please request a new one.", + ) + + @patch( + "authentik.flows.views.executor.to_stage_response", + TO_STAGE_RESPONSE_MOCK, + ) + def test_no_token_require_token(self): + """Test that accessing a REQUIRE_TOKEN flow without any token shows error""" + flow = create_test_flow( + FlowDesignation.RECOVERY, + authentication=FlowAuthenticationRequirement.REQUIRE_TOKEN, + ) + + url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}) + response = self.client.get(url) + self.assertStageResponse( + response, + flow, + component="ak-stage-access-denied", + error_message="This link is invalid or has expired. Please request a new one.", + ) diff --git a/authentik/flows/tests/test_planner.py b/authentik/flows/tests/test_planner.py index d0e16990b6..dc15dbae9a 100644 --- a/authentik/flows/tests/test_planner.py +++ b/authentik/flows/tests/test_planner.py @@ -26,6 +26,7 @@ from authentik.flows.models import ( ) from authentik.flows.planner import ( PLAN_CONTEXT_IS_REDIRECTED, + PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key, @@ -129,6 +130,22 @@ class TestFlowPlanner(TestCase): planner.allow_empty_flows = True planner.plan(request) + def test_authentication_require_token(self): + """Test flow authentication (require_token)""" + flow = create_test_flow() + flow.authentication = FlowAuthenticationRequirement.REQUIRE_TOKEN + request = self.request_factory.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), + ) + planner = FlowPlanner(flow) + planner.allow_empty_flows = True + + with self.assertRaises(FlowNonApplicableException): + planner.plan(request) + + context = {PLAN_CONTEXT_IS_RESTORED: True} + planner.plan(request, context) + @patch( "authentik.policies.engine.PolicyEngine.result", POLICY_RETURN_FALSE, diff --git a/authentik/flows/views/executor.py b/authentik/flows/views/executor.py index 9bd547392a..0b2ebdefdb 100644 --- a/authentik/flows/views/executor.py +++ b/authentik/flows/views/executor.py @@ -62,6 +62,7 @@ from authentik.policies.engine import PolicyEngine LOGGER = get_logger() # Argument used to redirect user after login NEXT_ARG_NAME = "next" + SESSION_KEY_PLAN = "authentik/flows/plan" SESSION_KEY_GET = "authentik/flows/get" SESSION_KEY_POST = "authentik/flows/post" diff --git a/authentik/lib/utils/db.py b/authentik/lib/utils/db.py index ef72d76652..9d40bea795 100644 --- a/authentik/lib/utils/db.py +++ b/authentik/lib/utils/db.py @@ -14,7 +14,16 @@ def chunked_queryset[T: Model](queryset: QuerySet[T], chunk_size: int = 1_000) - def get_chunks(qs: QuerySet) -> Generator[QuerySet[T]]: qs = qs.order_by("pk") pks = qs.values_list("pk", flat=True) - start_pk = pks[0] + # The outer queryset.exists() guard can race with a concurrent + # transaction that deletes the last matching row (or with a + # different isolation-level snapshot), so by the time this + # generator starts iterating the queryset may be empty and + # pks[0] would raise IndexError and crash the caller. Using + # .first() returns None on an empty queryset, which we bail + # out on cleanly. See goauthentik/authentik#21643. + start_pk = pks.first() + if start_pk is None: + return while True: try: end_pk = pks.filter(pk__gte=start_pk)[chunk_size] diff --git a/authentik/providers/oauth2/tests/test_authorize.py b/authentik/providers/oauth2/tests/test_authorize.py index c779eef052..2f5f70575a 100644 --- a/authentik/providers/oauth2/tests/test_authorize.py +++ b/authentik/providers/oauth2/tests/test_authorize.py @@ -141,26 +141,6 @@ class TestAuthorize(OAuthTestCase): OAuthAuthorizationParams.from_request(request) self.assertEqual(cm.exception.cause, "redirect_uri_forbidden_scheme") - def test_invalid_redirect_uri_empty(self): - """test missing/invalid redirect URI""" - provider = OAuth2Provider.objects.create( - name=generate_id(), - client_id="test", - authorization_flow=create_test_flow(), - redirect_uris=[], - ) - request = self.factory.get( - "/", - data={ - "response_type": "code", - "client_id": "test", - "redirect_uri": "+", - }, - ) - OAuthAuthorizationParams.from_request(request) - provider.refresh_from_db() - self.assertEqual(provider.redirect_uris, [RedirectURI(RedirectURIMatchingMode.STRICT, "+")]) - def test_invalid_redirect_uri_regex(self): """test missing/invalid redirect URI""" OAuth2Provider.objects.create( diff --git a/authentik/providers/oauth2/tests/test_device_backchannel.py b/authentik/providers/oauth2/tests/test_device_backchannel.py index 1e75b7f8ef..1c54ca54c4 100644 --- a/authentik/providers/oauth2/tests/test_device_backchannel.py +++ b/authentik/providers/oauth2/tests/test_device_backchannel.py @@ -6,10 +6,11 @@ from urllib.parse import quote from django.urls import reverse +from authentik.blueprints.tests import apply_blueprint from authentik.core.models import Application from authentik.core.tests.utils import create_test_flow from authentik.lib.generators import generate_id -from authentik.providers.oauth2.models import OAuth2Provider +from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider, ScopeMapping from authentik.providers.oauth2.tests.utils import OAuthTestCase @@ -110,3 +111,57 @@ class TesOAuth2DeviceBackchannel(OAuthTestCase): self.assertEqual(res.status_code, 200) body = loads(res.content.decode()) self.assertEqual(body["expires_in"], 60) + + @apply_blueprint("system/providers-oauth2.yaml") + def test_backchannel_scopes(self): + """Test backchannel""" + self.provider.property_mappings.set( + ScopeMapping.objects.filter( + managed__in=[ + "goauthentik.io/providers/oauth2/scope-openid", + "goauthentik.io/providers/oauth2/scope-email", + "goauthentik.io/providers/oauth2/scope-profile", + ] + ) + ) + creds = b64encode(f"{self.provider.client_id}:".encode()).decode() + res = self.client.post( + reverse("authentik_providers_oauth2:device"), + HTTP_AUTHORIZATION=f"Basic {creds}", + data={"scope": "openid email"}, + ) + self.assertEqual(res.status_code, 200) + body = loads(res.content.decode()) + self.assertEqual(body["expires_in"], 60) + token = DeviceToken.objects.filter(device_code=body["device_code"]).first() + self.assertIsNotNone(token) + self.assertEqual(len(token.scope), 2) + self.assertIn("openid", token.scope) + self.assertIn("email", token.scope) + + @apply_blueprint("system/providers-oauth2.yaml") + def test_backchannel_scopes_extra(self): + """Test backchannel""" + self.provider.property_mappings.set( + ScopeMapping.objects.filter( + managed__in=[ + "goauthentik.io/providers/oauth2/scope-openid", + "goauthentik.io/providers/oauth2/scope-email", + "goauthentik.io/providers/oauth2/scope-profile", + ] + ) + ) + creds = b64encode(f"{self.provider.client_id}:".encode()).decode() + res = self.client.post( + reverse("authentik_providers_oauth2:device"), + HTTP_AUTHORIZATION=f"Basic {creds}", + data={"scope": "openid email foo"}, + ) + self.assertEqual(res.status_code, 200) + body = loads(res.content.decode()) + self.assertEqual(body["expires_in"], 60) + token = DeviceToken.objects.filter(device_code=body["device_code"]).first() + self.assertIsNotNone(token) + self.assertEqual(len(token.scope), 2) + self.assertIn("openid", token.scope) + self.assertIn("email", token.scope) diff --git a/authentik/providers/oauth2/views/authorize.py b/authentik/providers/oauth2/views/authorize.py index 7793abfc43..5bd1cbcc7b 100644 --- a/authentik/providers/oauth2/views/authorize.py +++ b/authentik/providers/oauth2/views/authorize.py @@ -59,9 +59,7 @@ from authentik.providers.oauth2.models import ( AuthorizationCode, GrantTypes, OAuth2Provider, - RedirectURI, RedirectURIMatchingMode, - RedirectURIType, ResponseMode, ResponseTypes, ScopeMapping, @@ -197,18 +195,6 @@ class OAuthAuthorizationParams: LOGGER.warning("Missing redirect uri.") raise RedirectUriError("", allowed_redirect_urls).with_cause("redirect_uri_missing") - if len(allowed_redirect_urls) < 1: - LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri) - self.provider.redirect_uris = [ - RedirectURI( - RedirectURIMatchingMode.STRICT, - self.redirect_uri, - RedirectURIType.AUTHORIZATION, - ) - ] - self.provider.save() - allowed_redirect_urls = self.provider.authorization_redirect_uris - match_found = False for allowed in allowed_redirect_urls: if allowed.matching_mode == RedirectURIMatchingMode.STRICT: diff --git a/authentik/providers/oauth2/views/device_backchannel.py b/authentik/providers/oauth2/views/device_backchannel.py index d8e73f9894..301f36492d 100644 --- a/authentik/providers/oauth2/views/device_backchannel.py +++ b/authentik/providers/oauth2/views/device_backchannel.py @@ -15,7 +15,7 @@ from authentik.core.models import Application from authentik.lib.config import CONFIG from authentik.lib.utils.time import timedelta_from_string from authentik.providers.oauth2.errors import DeviceCodeError -from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider +from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider, ScopeMapping from authentik.providers.oauth2.utils import TokenResponse, extract_client_auth from authentik.providers.oauth2.views.device_init import QS_KEY_CODE @@ -28,7 +28,7 @@ class DeviceView(View): client_id: str provider: OAuth2Provider - scopes: list[str] = [] + scopes: set[str] = [] def parse_request(self): """Parse incoming request""" @@ -44,7 +44,21 @@ class DeviceView(View): raise DeviceCodeError("invalid_client") from None self.provider = provider self.client_id = client_id - self.scopes = self.request.POST.get("scope", "").split(" ") + + scopes_to_check = set(self.request.POST.get("scope", "").split()) + default_scope_names = set( + ScopeMapping.objects.filter(provider__in=[self.provider]).values_list( + "scope_name", flat=True + ) + ) + self.scopes = scopes_to_check + if not scopes_to_check.issubset(default_scope_names): + LOGGER.info( + "Application requested scopes not configured, setting to overlap", + scope_allowed=default_scope_names, + scope_given=self.scopes, + ) + self.scopes = self.scopes.intersection(default_scope_names) def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: throttle = AnonRateThrottle() diff --git a/authentik/sources/ldap/models.py b/authentik/sources/ldap/models.py index f7fe889a97..05492a363a 100644 --- a/authentik/sources/ldap/models.py +++ b/authentik/sources/ldap/models.py @@ -12,7 +12,13 @@ from django.db import connection, models from django.templatetags.static import static from django.utils.translation import gettext_lazy as _ from ldap3 import ALL, NONE, RANDOM, Connection, Server, ServerPool, Tls -from ldap3.core.exceptions import LDAPException, LDAPInsufficientAccessRightsResult, LDAPSchemaError +from ldap3.core.exceptions import ( + LDAPAdminLimitExceededResult, + LDAPAttributeError, + LDAPException, + LDAPInsufficientAccessRightsResult, + LDAPSchemaError, +) from rest_framework.serializers import Serializer from structlog.stdlib import get_logger @@ -278,10 +284,17 @@ class LDAPSource(IncomingSyncSource): successful = conn.bind() if successful: return conn - except (LDAPSchemaError, LDAPInsufficientAccessRightsResult) as exc: - # Schema error, so try connecting without schema info + except ( + LDAPSchemaError, + LDAPInsufficientAccessRightsResult, + LDAPAdminLimitExceededResult, + LDAPAttributeError, + ) as exc: + # Schema error or rate limit during schema fetch, retry without schema info # See https://github.com/goauthentik/authentik/issues/4590 # See also https://github.com/goauthentik/authentik/issues/3399 + # LDAPAdminLimitExceededResult: Google Secure LDAP rate-limits schema queries + # LDAPAttributeError: Google Secure LDAP returns unsupported attrs in schema if server_kwargs.get("get_info", ALL) == NONE: LOGGER.warning("Failed to connect after schema downgrade", source=self, exc=exc) raise exc diff --git a/authentik/sources/oauth/api/source.py b/authentik/sources/oauth/api/source.py index b46f1e7f60..0075efd46c 100644 --- a/authentik/sources/oauth/api/source.py +++ b/authentik/sources/oauth/api/source.py @@ -17,7 +17,7 @@ from authentik.core.api.sources import SourceSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import PassiveSerializer from authentik.lib.utils.http import get_http_session -from authentik.sources.oauth.models import OAuthSource +from authentik.sources.oauth.models import OAuthSource, PKCEMethod from authentik.sources.oauth.types.registry import SourceType, registry @@ -83,13 +83,24 @@ class OAuthSourceSerializer(SourceSerializer): "authorization_url": "authorization_endpoint", "access_token_url": "token_endpoint", "profile_url": "userinfo_endpoint", - "pkce": "code_challenge_methods_supported", } for ak_key, oidc_key in field_map.items(): # Don't overwrite user-set values if ak_key in attrs and attrs[ak_key]: continue attrs[ak_key] = config.get(oidc_key, "") + # code_challenge_methods_supported is a list per RFC 8414, not a + # single method. Pick one (prefer S256, the RFC-recommended method) + # rather than letting the list round-trip into the pkce TextField + # and later str() into the authorize URL as "['plain', 'S256']". + if not attrs.get("pkce"): + supported_methods = config.get("code_challenge_methods_supported") or [] + attrs["pkce"] = PKCEMethod.NONE + if isinstance(supported_methods, list): + if PKCEMethod.S256 in supported_methods: + attrs["pkce"] = PKCEMethod.S256 + elif PKCEMethod.PLAIN in supported_methods: + attrs["pkce"] = PKCEMethod.PLAIN inferred_oidc_jwks_url = config.get("jwks_uri", "") # Prefer user-entered URL to inferred URL to default URL diff --git a/authentik/sources/oauth/tests/test_views.py b/authentik/sources/oauth/tests/test_views.py index ebc4a4efa3..e75885f542 100644 --- a/authentik/sources/oauth/tests/test_views.py +++ b/authentik/sources/oauth/tests/test_views.py @@ -79,6 +79,7 @@ class TestOAuthSource(APITestCase): "token_endpoint": "http://mock/oauth/token", "userinfo_endpoint": "http://mock/oauth/userinfo", "jwks_uri": "http://mock/oauth/discovery/keys", + "code_challenge_methods_supported": ["S256"], } jwks_config = {"keys": []} with Mocker() as mocker: @@ -109,6 +110,7 @@ class TestOAuthSource(APITestCase): serializer.validated_data["oidc_jwks_url"], "http://mock/oauth/discovery/keys" ) self.assertEqual(serializer.validated_data["oidc_jwks"], jwks_config) + self.assertEqual(serializer.validated_data["pkce"], PKCEMethod.S256) def test_api_validate_openid_connect_invalid(self): """Test API validation (with OIDC endpoints)""" diff --git a/authentik/stages/user_write/stage.py b/authentik/stages/user_write/stage.py index e3f95506d1..c21a85cf5f 100644 --- a/authentik/stages/user_write/stage.py +++ b/authentik/stages/user_write/stage.py @@ -36,6 +36,14 @@ class UserWriteStageView(StageView): super().__init__(executor, **kwargs) self.disallowed_user_attributes = [ "groups", + # Block attribute writes that would otherwise land on the model's + # primary key. An IdP that returns an `id` claim (mocksaml is one + # example) used to crash the enrollment flow with + # ValueError: Field 'id' expected a number but got '' + # because hasattr(user, "id") is true and setattr(user, "id", ...) + # was taken unchecked. See #21580. + "id", + "pk", ] @staticmethod diff --git a/authentik/stages/user_write/tests.py b/authentik/stages/user_write/tests.py index c4b5a87c0b..41fbe2b459 100644 --- a/authentik/stages/user_write/tests.py +++ b/authentik/stages/user_write/tests.py @@ -315,6 +315,34 @@ class TestUserWriteStage(FlowTestCase): component="ak-stage-access-denied", ) + def test_user_update_ignores_id_from_idp(self): + """IdP-supplied `id`/`pk` attributes must not land on the model + primary key and crash user save (#21580).""" + existing = User.objects.create(username="unittest", email="test@goauthentik.io") + original_pk = existing.pk + + plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) + plan.context[PLAN_CONTEXT_PENDING_USER] = existing + plan.context[PLAN_CONTEXT_PROMPT] = { + "username": "idp-user", + # Hex string from a SAML IdP; would previously crash with + # ValueError: Field 'id' expected a number but got ''. + "id": "1dda9fb491dc01bd24d2423ba2f22ae561f56ddf2376b29a11c80281d21201f9", + "pk": "also-not-an-int", + } + session = self.client.session + session[SESSION_KEY_PLAN] = plan + session.save() + + response = self.client.post( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) + ) + + self.assertEqual(response.status_code, 200) + self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) + user = User.objects.get(username="idp-user") + self.assertEqual(user.pk, original_pk) + def test_write_attribute(self): """Test write_attribute""" user = create_test_admin_user() diff --git a/blueprints/schema.json b/blueprints/schema.json index 82673512b6..f290b1eef1 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -5644,6 +5644,7 @@ "authentik_endpoints_connectors_agent.add_agentconnector", "authentik_endpoints_connectors_agent.add_agentdeviceconnection", "authentik_endpoints_connectors_agent.add_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.add_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.add_applenonce", "authentik_endpoints_connectors_agent.add_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.add_devicetoken", @@ -5651,6 +5652,7 @@ "authentik_endpoints_connectors_agent.change_agentconnector", "authentik_endpoints_connectors_agent.change_agentdeviceconnection", "authentik_endpoints_connectors_agent.change_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.change_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.change_applenonce", "authentik_endpoints_connectors_agent.change_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.change_devicetoken", @@ -5658,6 +5660,7 @@ "authentik_endpoints_connectors_agent.delete_agentconnector", "authentik_endpoints_connectors_agent.delete_agentdeviceconnection", "authentik_endpoints_connectors_agent.delete_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.delete_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.delete_applenonce", "authentik_endpoints_connectors_agent.delete_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.delete_devicetoken", @@ -5665,6 +5668,7 @@ "authentik_endpoints_connectors_agent.view_agentconnector", "authentik_endpoints_connectors_agent.view_agentdeviceconnection", "authentik_endpoints_connectors_agent.view_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.view_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.view_applenonce", "authentik_endpoints_connectors_agent.view_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.view_devicetoken", @@ -8426,7 +8430,8 @@ "require_unauthenticated", "require_superuser", "require_redirect", - "require_outpost" + "require_outpost", + "require_token" ], "title": "Authentication", "description": "Required level of authentication and authorization to access a flow." @@ -11319,6 +11324,7 @@ "authentik_endpoints_connectors_agent.add_agentconnector", "authentik_endpoints_connectors_agent.add_agentdeviceconnection", "authentik_endpoints_connectors_agent.add_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.add_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.add_applenonce", "authentik_endpoints_connectors_agent.add_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.add_devicetoken", @@ -11326,6 +11332,7 @@ "authentik_endpoints_connectors_agent.change_agentconnector", "authentik_endpoints_connectors_agent.change_agentdeviceconnection", "authentik_endpoints_connectors_agent.change_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.change_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.change_applenonce", "authentik_endpoints_connectors_agent.change_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.change_devicetoken", @@ -11333,6 +11340,7 @@ "authentik_endpoints_connectors_agent.delete_agentconnector", "authentik_endpoints_connectors_agent.delete_agentdeviceconnection", "authentik_endpoints_connectors_agent.delete_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.delete_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.delete_applenonce", "authentik_endpoints_connectors_agent.delete_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.delete_devicetoken", @@ -11340,6 +11348,7 @@ "authentik_endpoints_connectors_agent.view_agentconnector", "authentik_endpoints_connectors_agent.view_agentdeviceconnection", "authentik_endpoints_connectors_agent.view_agentdeviceuserbinding", + "authentik_endpoints_connectors_agent.view_appleindependentsecureenclave", "authentik_endpoints_connectors_agent.view_applenonce", "authentik_endpoints_connectors_agent.view_deviceauthenticationtoken", "authentik_endpoints_connectors_agent.view_devicetoken", diff --git a/go.mod b/go.mod index 098c676dd6..5fb7aa1c43 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( beryju.io/radius-eap v0.1.0 github.com/avast/retry-go/v4 v4.7.0 github.com/coreos/go-oidc/v3 v3.18.0 - github.com/getsentry/sentry-go v0.44.1 + github.com/getsentry/sentry-go v0.45.1 github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-ldap/ldap/v3 v3.4.13 - github.com/go-openapi/runtime v0.29.3 + github.com/go-openapi/runtime v0.29.4 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.2 @@ -19,11 +19,11 @@ require ( github.com/gorilla/sessions v1.4.0 github.com/gorilla/websocket v1.5.3 github.com/grafana/pyroscope-go v1.2.8 - github.com/jackc/pgx/v5 v5.9.1 + github.com/jackc/pgx/v5 v5.9.2 github.com/jellydator/ttlcache/v3 v3.4.0 github.com/mitchellh/mapstructure v1.5.0 github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 - github.com/pires/go-proxyproto v0.11.0 + github.com/pires/go-proxyproto v0.12.0 github.com/prometheus/client_golang v1.23.2 github.com/sethvargo/go-envconfig v1.3.0 github.com/sirupsen/logrus v1.9.4 @@ -51,21 +51,21 @@ require ( github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.24.3 // indirect + github.com/go-openapi/analysis v0.25.0 // indirect github.com/go-openapi/errors v0.22.7 // indirect github.com/go-openapi/jsonpointer v0.22.5 // indirect github.com/go-openapi/jsonreference v0.21.5 // indirect github.com/go-openapi/loads v0.23.3 // indirect github.com/go-openapi/spec v0.22.4 // indirect - github.com/go-openapi/strfmt v0.26.0 // indirect - github.com/go-openapi/swag/conv v0.25.5 // indirect - github.com/go-openapi/swag/fileutils v0.25.5 // indirect + github.com/go-openapi/strfmt v0.26.1 // indirect + github.com/go-openapi/swag/conv v0.26.0 // indirect + github.com/go-openapi/swag/fileutils v0.26.0 // indirect github.com/go-openapi/swag/jsonname v0.25.5 // indirect - github.com/go-openapi/swag/jsonutils v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.26.0 // indirect github.com/go-openapi/swag/loading v0.25.5 // indirect github.com/go-openapi/swag/mangling v0.25.5 // indirect - github.com/go-openapi/swag/stringutils v0.25.5 // indirect - github.com/go-openapi/swag/typeutils v0.25.5 // indirect + github.com/go-openapi/swag/stringutils v0.26.0 // indirect + github.com/go-openapi/swag/typeutils v0.26.0 // indirect github.com/go-openapi/swag/yamlutils v0.25.5 // indirect github.com/go-openapi/validate v0.25.2 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect @@ -85,15 +85,15 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/spf13/pflag v1.0.9 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.41.0 // indirect - go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/trace v1.41.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b471badbf7..81463b5742 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/getsentry/sentry-go v0.44.1 h1:/cPtrA5qB7uMRrhgSn9TYtcEF36auGP3Y6+ThvD/yaI= -github.com/getsentry/sentry-go v0.44.1/go.mod h1:XDotiNZbgf5U8bPDUAfvcFmOnMQQceESxyKaObSssW0= +github.com/getsentry/sentry-go v0.45.1 h1:9rfzJtGiJG+MGIaWZXidDGHcH5GU1Z5y0WVJGf9nysw= +github.com/getsentry/sentry-go v0.45.1/go.mod h1:XDotiNZbgf5U8bPDUAfvcFmOnMQQceESxyKaObSssW0= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -41,8 +41,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.24.3 h1:a1hrvMr8X0Xt69KP5uVTu5jH62DscmDifrLzNglAayk= -github.com/go-openapi/analysis v0.24.3/go.mod h1:Nc+dWJ/FxZbhSow5Yh3ozg5CLJioB+XXT6MdLvJUsUw= +github.com/go-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvCB7bCs= +github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE= github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= @@ -51,36 +51,36 @@ github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ= github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA= -github.com/go-openapi/runtime v0.29.3 h1:h5twGaEqxtQg40ePiYm9vFFH1q06Czd7Ot6ufdK0w/Y= -github.com/go-openapi/runtime v0.29.3/go.mod h1:8A1W0/L5eyNJvKciqZtvIVQvYO66NlB7INMSZ9bw/oI= +github.com/go-openapi/runtime v0.29.4 h1:k2lDxrGoSAJRdhFG2tONKMpkizY/4X1cciSdtzk4Jjo= +github.com/go-openapi/runtime v0.29.4/go.mod h1:K0k/2raY6oqXJnZAgWJB2i/12QKrhUKpZcH4PfV9P18= github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= -github.com/go-openapi/strfmt v0.26.0 h1:SDdQLyOEqu8W96rO1FRG1fuCtVyzmukky0zcD6gMGLU= -github.com/go-openapi/strfmt v0.26.0/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y= -github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= -github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= -github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= -github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= +github.com/go-openapi/strfmt v0.26.1 h1:7zGCHji7zSYDC2tCXIusoxYQz/48jAf2q+sF6wXTG+c= +github.com/go-openapi/strfmt v0.26.1/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y= +github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= +github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= +github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= +github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= -github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= -github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= +github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= -github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= -github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= -github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= -github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= +github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= +github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= +github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q= -github.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw= -github.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= +github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= +github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0= github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= @@ -117,8 +117,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= -github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -159,8 +159,8 @@ github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4= -github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= +github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM= +github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -199,14 +199,14 @@ github.com/wwt/guac v1.3.2/go.mod h1:eKm+NrnK7A88l4UBEcYNpZQGMpZRryYKoz4D/0/n1C0 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= -go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= -go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= -go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= -go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= -go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= -go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= @@ -216,8 +216,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab h1:628ME69lBm9C6JY2wXhAph/yjN3jezx1z7BIDLUwxjo= golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -227,8 +227,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -245,8 +245,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -258,8 +258,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/internal/outpost/ak/api.go b/internal/outpost/ak/api.go index 8cb5c4830a..083fd66c9c 100644 --- a/internal/outpost/ak/api.go +++ b/internal/outpost/ak/api.go @@ -11,6 +11,7 @@ import ( "os" "os/signal" "runtime" + "sync" "syscall" "time" @@ -45,6 +46,7 @@ type APIController struct { reloadOffset time.Duration eventConn *websocket.Conn + eventConnMu sync.Mutex lastWsReconnect time.Time wsIsReconnecting bool eventHandlers []EventHandler diff --git a/internal/outpost/ak/api_event.go b/internal/outpost/ak/api_event.go index 3bc64664f3..b9dd0901a6 100644 --- a/internal/outpost/ak/api_event.go +++ b/internal/outpost/ak/api_event.go @@ -77,7 +77,12 @@ func (ac *APIController) initEvent(outpostUUID string, attempt int) error { Instruction: EventKindHello, Args: ac.getEventPingArgs(), } + // Serialize this write against concurrent SendEventHello callers (health + // ticker, RAC handlers) sharing the same *websocket.Conn. Gorilla's Conn + // does not permit concurrent writes. + ac.eventConnMu.Lock() err = ws.WriteJSON(msg) + ac.eventConnMu.Unlock() if err != nil { ac.logger.WithField("logger", "authentik.outpost.events").WithError(err).Warning("Failed to hello to authentik") return err @@ -91,7 +96,9 @@ func (ac *APIController) initEvent(outpostUUID string, attempt int) error { func (ac *APIController) Shutdown() { // Cleanly close the connection by sending a close message and then // waiting (with timeout) for the server to close the connection. + ac.eventConnMu.Lock() err := ac.eventConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + ac.eventConnMu.Unlock() if err != nil { ac.logger.WithError(err).Warning("failed to write close message") return @@ -252,6 +259,10 @@ func (a *APIController) SendEventHello(args map[string]any) error { Instruction: EventKindHello, Args: allArgs, } + // Gorilla *websocket.Conn does not permit concurrent writes. This method + // is invoked from the health ticker and from RAC session handlers. + a.eventConnMu.Lock() err := a.eventConn.WriteJSON(aliveMsg) + a.eventConnMu.Unlock() return err } diff --git a/lifecycle/aws/package-lock.json b/lifecycle/aws/package-lock.json index 6b49579b0b..4cd46895c4 100644 --- a/lifecycle/aws/package-lock.json +++ b/lifecycle/aws/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "devDependencies": { - "aws-cdk": "^2.1118.0", + "aws-cdk": "^2.1118.2", "cross-env": "^10.1.0" }, "engines": { @@ -25,9 +25,9 @@ "license": "MIT" }, "node_modules/aws-cdk": { - "version": "2.1118.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1118.0.tgz", - "integrity": "sha512-Tfd865GRewDTXIbTVtix/l+v8t3rZENvdHcQQZS2wXYVXfHzljULFXe9JKkgZUNDPB1zo9tSBUu8jjiHRm7nWg==", + "version": "2.1118.2", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1118.2.tgz", + "integrity": "sha512-jHuShSx0JI14enDz2Hk2Qe0LYTDPzLyF2nBhWCvoXyRCpz31sI3XsCh4KO5ZXKfw9ET0bHvDTVnMZQPBpswg8A==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/lifecycle/aws/package.json b/lifecycle/aws/package.json index 28ce9fc469..0dd27badc9 100644 --- a/lifecycle/aws/package.json +++ b/lifecycle/aws/package.json @@ -7,7 +7,7 @@ "aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml" }, "devDependencies": { - "aws-cdk": "^2.1118.0", + "aws-cdk": "^2.1118.2", "cross-env": "^10.1.0" }, "engines": { diff --git a/lifecycle/container/Dockerfile b/lifecycle/container/Dockerfile index 6e67ec0927..a84f41912c 100644 --- a/lifecycle/container/Dockerfile +++ b/lifecycle/container/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Stage: Build webui -FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-trixie-slim@sha256:9707cd4542f400df5078df04f9652a272429112f15202d22b5b8bdd148df494f AS node-builder +FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-trixie-slim@sha256:735dd688da64d22ebd9dd374b3e7e5a874635668fd2a6ec20ca1f99264294086 AS node-builder ARG GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH @@ -29,7 +29,7 @@ RUN npm run build && \ npm run build:sfe # Stage: Build go proxy -FROM docker.io/library/golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS go-builder +FROM docker.io/library/golang:1.26.2-trixie@sha256:982ae929f9a74083a242c6e25d19d7d9ed78c6e97fab639a119e90707ba819e2 AS go-builder ARG TARGETARCH ARG TARGETVARIANT diff --git a/lifecycle/container/ldap.Dockerfile b/lifecycle/container/ldap.Dockerfile index 8bebfe02e1..b4eec5d7a1 100644 --- a/lifecycle/container/ldap.Dockerfile +++ b/lifecycle/container/ldap.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Stage 1: Build -FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS builder +FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:982ae929f9a74083a242c6e25d19d7d9ed78c6e97fab639a119e90707ba819e2 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/lifecycle/container/proxy.Dockerfile b/lifecycle/container/proxy.Dockerfile index a74604995a..cbf58ab1f0 100644 --- a/lifecycle/container/proxy.Dockerfile +++ b/lifecycle/container/proxy.Dockerfile @@ -21,7 +21,7 @@ COPY web . RUN npm run build-proxy # Stage 2: Build -FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS builder +FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:982ae929f9a74083a242c6e25d19d7d9ed78c6e97fab639a119e90707ba819e2 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/lifecycle/container/rac.Dockerfile b/lifecycle/container/rac.Dockerfile index d25a5dc854..7d2eaf4a3e 100644 --- a/lifecycle/container/rac.Dockerfile +++ b/lifecycle/container/rac.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Stage 1: Build -FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS builder +FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:982ae929f9a74083a242c6e25d19d7d9ed78c6e97fab639a119e90707ba819e2 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/lifecycle/container/radius.Dockerfile b/lifecycle/container/radius.Dockerfile index 1a797f63d6..a5f38333d8 100644 --- a/lifecycle/container/radius.Dockerfile +++ b/lifecycle/container/radius.Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Stage 1: Build -FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS builder +FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.2-trixie@sha256:982ae929f9a74083a242c6e25d19d7d9ed78c6e97fab639a119e90707ba819e2 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 0e1271ef51..009f386698 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-08 00:28+0000\n" +"POT-Creation-Date: 2026-04-23 00:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -732,6 +732,14 @@ msgstr "" msgid "Apple Nonces" msgstr "" +#: authentik/endpoints/connectors/agent/models.py +msgid "Apple Independent Secure Enclave" +msgstr "" + +#: authentik/endpoints/connectors/agent/models.py +msgid "Apple Independent Secure Enclaves" +msgstr "" + #: authentik/endpoints/facts.py msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'" msgstr "" @@ -1571,6 +1579,10 @@ msgstr "" msgid "Flow Tokens" msgstr "" +#: authentik/flows/planner.py +msgid "This link is invalid or has expired. Please request a new one." +msgstr "" + #: authentik/flows/views/executor.py msgid "Invalid next URL" msgstr "" diff --git a/locale/en/dictionaries/idp.txt b/locale/en/dictionaries/idp.txt index 34bd19b61e..6c6c63b665 100644 --- a/locale/en/dictionaries/idp.txt +++ b/locale/en/dictionaries/idp.txt @@ -4,3 +4,4 @@ Yubi Yubikey Yubikeys mycorp +mocksaml diff --git a/packages/ak-common/src/api.rs b/packages/ak-common/src/api.rs index 46ad8a5322..0624bd3ad4 100644 --- a/packages/ak-common/src/api.rs +++ b/packages/ak-common/src/api.rs @@ -19,18 +19,19 @@ impl ServerConfig { .host .clone() .ok_or_else(|| eyre!("environment variable `AUTHENTIK_HOST` not set"))?; - let host = if host.ends_with('/') { - host - } else { - format!("{host}/") - }; - let host = host.parse()?; + let mut host: Url = host.parse()?; let token = config::get() .token .clone() .ok_or_else(|| eyre!("environment variable `AUTHENTIK_TOKEN` not set"))?; let insecure = config::get().insecure.unwrap_or(false); + if !host.path().ends_with('/') { + host.path_segments_mut() + .map_err(|()| eyre!("URL cannot be a base"))? + .push(""); + } + Ok(Self { host, token, @@ -43,7 +44,7 @@ impl ServerConfig { pub fn make_config() -> Result { let server_config = ServerConfig::new()?; - let base_path = format!("{}api/v3", server_config.host); + let base_path = server_config.host.join("api/v3")?.into(); let client = reqwest::ClientBuilder::new() .tls_danger_accept_invalid_hostnames(server_config.insecure) @@ -95,3 +96,135 @@ where Ok(results) } + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::{ServerConfig, make_config}; + use crate::config; + + #[test] + fn server_config_no_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000", + "token": "token", + })) + .expect("failed to set config"); + + let server_config = ServerConfig::new().expect("failed to create server config"); + + assert_eq!(server_config.host.as_str(), "http://localhost:9000/"); + } + + #[test] + fn server_config_with_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/", + "token": "token", + })) + .expect("failed to set config"); + + let server_config = ServerConfig::new().expect("failed to create server config"); + + assert_eq!(server_config.host.as_str(), "http://localhost:9000/"); + } + + #[test] + fn server_config_with_path_no_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/authentik", + "token": "token", + })) + .expect("failed to set config"); + + let server_config = ServerConfig::new().expect("failed to create server config"); + + assert_eq!( + server_config.host.as_str(), + "http://localhost:9000/authentik/" + ); + } + + #[test] + fn server_config_with_path_and_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/authentik/", + "token": "token", + })) + .expect("failed to set config"); + + let server_config = ServerConfig::new().expect("failed to create server config"); + + assert_eq!( + server_config.host.as_str(), + "http://localhost:9000/authentik/" + ); + } + + #[test] + fn make_config_no_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000", + "token": "token", + })) + .expect("failed to set config"); + + let api_config = make_config().expect("failed to make config"); + + assert_eq!(api_config.base_path, "http://localhost:9000/api/v3"); + } + + #[test] + fn make_config_with_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/", + "token": "token", + })) + .expect("failed to set config"); + + let api_config = make_config().expect("failed to make config"); + + assert_eq!(api_config.base_path, "http://localhost:9000/api/v3"); + } + + #[test] + fn make_config_with_path_no_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/authentik", + "token": "token", + })) + .expect("failed to set config"); + + let api_config = make_config().expect("failed to make config"); + + assert_eq!( + api_config.base_path, + "http://localhost:9000/authentik/api/v3" + ); + } + + #[test] + fn make_config_with_path_and_trailing_slash() { + config::init().expect("failed to init config"); + config::set(json!({ + "host": "http://localhost:9000/authentik/", + "token": "token", + })) + .expect("failed to set config"); + + let api_config = make_config().expect("failed to make config"); + + assert_eq!( + api_config.base_path, + "http://localhost:9000/authentik/api/v3" + ); + } +} diff --git a/packages/ak-common/src/config/mod.rs b/packages/ak-common/src/config/mod.rs index 63b4ccefd7..7d5c4ad612 100644 --- a/packages/ak-common/src/config/mod.rs +++ b/packages/ak-common/src/config/mod.rs @@ -400,7 +400,7 @@ mod tests { let arbiter = tasks.arbiter(); let mut events_rx = arbiter.events_subscribe(); - super::run(&mut tasks).expect("failed to start watcher"); + super::start(&mut tasks).expect("failed to start watcher"); assert_eq!(super::get().secret_key, "my_secret_key"); assert_eq!(super::get().postgresql.password, "my_postgres_pass"); diff --git a/packages/client-go/client.go b/packages/client-go/client.go index 2dfb593c04..a8be2d57f7 100644 --- a/packages/client-go/client.go +++ b/packages/client-go/client.go @@ -433,6 +433,11 @@ func (c *APIClient) prepareRequest( // Walk through any authentication. + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + } for header, value := range c.cfg.DefaultHeader { diff --git a/packages/client-go/configuration.go b/packages/client-go/configuration.go index 051b41b3dd..754d7bed2b 100644 --- a/packages/client-go/configuration.go +++ b/packages/client-go/configuration.go @@ -29,6 +29,9 @@ func (c contextKey) String() string { } var ( + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + // ContextServerIndex uses a server configuration from the index. ContextServerIndex = contextKey("serverIndex") diff --git a/packages/client-ts/src/apis/EndpointsApi.ts b/packages/client-ts/src/apis/EndpointsApi.ts index 00cd7ef9e2..1e17887fc2 100644 --- a/packages/client-ts/src/apis/EndpointsApi.ts +++ b/packages/client-ts/src/apis/EndpointsApi.ts @@ -21,6 +21,8 @@ import type { AgentPSSODeviceRegistrationResponse, AgentPSSOUserRegistrationRequest, AgentTokenResponse, + AppleIndependentSecureEnclave, + AppleIndependentSecureEnclaveRequest, Connector, DeviceAccessGroup, DeviceAccessGroupRequest, @@ -41,6 +43,7 @@ import type { MDMConfigRequest, MDMConfigResponse, PaginatedAgentConnectorList, + PaginatedAppleIndependentSecureEnclaveList, PaginatedConnectorList, PaginatedDeviceAccessGroupList, PaginatedDeviceUserBindingList, @@ -49,6 +52,7 @@ import type { PaginatedFleetConnectorList, PaginatedGoogleChromeConnectorList, PatchedAgentConnectorRequest, + PatchedAppleIndependentSecureEnclaveRequest, PatchedDeviceAccessGroupRequest, PatchedDeviceUserBindingRequest, PatchedEndpointDeviceRequest, @@ -69,6 +73,8 @@ import { AgentPSSODeviceRegistrationResponseFromJSON, AgentPSSOUserRegistrationRequestToJSON, AgentTokenResponseFromJSON, + AppleIndependentSecureEnclaveFromJSON, + AppleIndependentSecureEnclaveRequestToJSON, ConnectorFromJSON, DeviceAccessGroupFromJSON, DeviceAccessGroupRequestToJSON, @@ -89,6 +95,7 @@ import { MDMConfigRequestToJSON, MDMConfigResponseFromJSON, PaginatedAgentConnectorListFromJSON, + PaginatedAppleIndependentSecureEnclaveListFromJSON, PaginatedConnectorListFromJSON, PaginatedDeviceAccessGroupListFromJSON, PaginatedDeviceUserBindingListFromJSON, @@ -97,6 +104,7 @@ import { PaginatedFleetConnectorListFromJSON, PaginatedGoogleChromeConnectorListFromJSON, PatchedAgentConnectorRequestToJSON, + PatchedAppleIndependentSecureEnclaveRequestToJSON, PatchedDeviceAccessGroupRequestToJSON, PatchedDeviceUserBindingRequestToJSON, PatchedEndpointDeviceRequestToJSON, @@ -201,6 +209,41 @@ export interface EndpointsAgentsEnrollmentTokensViewKeyRetrieveRequest { tokenUuid: string; } +export interface EndpointsAgentsPssoIseCreateRequest { + appleIndependentSecureEnclaveRequest: AppleIndependentSecureEnclaveRequest; +} + +export interface EndpointsAgentsPssoIseDestroyRequest { + uuid: string; +} + +export interface EndpointsAgentsPssoIseListRequest { + appleEnclaveKeyId?: string; + ordering?: string; + page?: number; + pageSize?: number; + search?: string; + user?: number; +} + +export interface EndpointsAgentsPssoIsePartialUpdateRequest { + uuid: string; + patchedAppleIndependentSecureEnclaveRequest?: PatchedAppleIndependentSecureEnclaveRequest; +} + +export interface EndpointsAgentsPssoIseRetrieveRequest { + uuid: string; +} + +export interface EndpointsAgentsPssoIseUpdateRequest { + uuid: string; + appleIndependentSecureEnclaveRequest: AppleIndependentSecureEnclaveRequest; +} + +export interface EndpointsAgentsPssoIseUsedByListRequest { + uuid: string; +} + export interface EndpointsAgentsPssoRegisterDeviceCreateRequest { agentPSSODeviceRegistrationRequest: AgentPSSODeviceRegistrationRequest; } @@ -1822,6 +1865,515 @@ export class EndpointsApi extends runtime.BaseAPI { return await response.value(); } + /** + * Creates request options for endpointsAgentsPssoIseCreate without sending the request + */ + async endpointsAgentsPssoIseCreateRequestOpts( + requestParameters: EndpointsAgentsPssoIseCreateRequest, + ): Promise { + if (requestParameters["appleIndependentSecureEnclaveRequest"] == null) { + throw new runtime.RequiredError( + "appleIndependentSecureEnclaveRequest", + 'Required parameter "appleIndependentSecureEnclaveRequest" was null or undefined when calling endpointsAgentsPssoIseCreate().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters["Content-Type"] = "application/json"; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/`; + + return { + path: urlPath, + method: "POST", + headers: headerParameters, + query: queryParameters, + body: AppleIndependentSecureEnclaveRequestToJSON( + requestParameters["appleIndependentSecureEnclaveRequest"], + ), + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseCreateRaw( + requestParameters: EndpointsAgentsPssoIseCreateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = + await this.endpointsAgentsPssoIseCreateRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => + AppleIndependentSecureEnclaveFromJSON(jsonValue), + ); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseCreate( + requestParameters: EndpointsAgentsPssoIseCreateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.endpointsAgentsPssoIseCreateRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Creates request options for endpointsAgentsPssoIseDestroy without sending the request + */ + async endpointsAgentsPssoIseDestroyRequestOpts( + requestParameters: EndpointsAgentsPssoIseDestroyRequest, + ): Promise { + if (requestParameters["uuid"] == null) { + throw new runtime.RequiredError( + "uuid", + 'Required parameter "uuid" was null or undefined when calling endpointsAgentsPssoIseDestroy().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/{uuid}/`; + urlPath = urlPath.replace( + `{${"uuid"}}`, + encodeURIComponent(String(requestParameters["uuid"])), + ); + + return { + path: urlPath, + method: "DELETE", + headers: headerParameters, + query: queryParameters, + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseDestroyRaw( + requestParameters: EndpointsAgentsPssoIseDestroyRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = + await this.endpointsAgentsPssoIseDestroyRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseDestroy( + requestParameters: EndpointsAgentsPssoIseDestroyRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + await this.endpointsAgentsPssoIseDestroyRaw(requestParameters, initOverrides); + } + + /** + * Creates request options for endpointsAgentsPssoIseList without sending the request + */ + async endpointsAgentsPssoIseListRequestOpts( + requestParameters: EndpointsAgentsPssoIseListRequest, + ): Promise { + const queryParameters: any = {}; + + if (requestParameters["appleEnclaveKeyId"] != null) { + queryParameters["apple_enclave_key_id"] = requestParameters["appleEnclaveKeyId"]; + } + + if (requestParameters["ordering"] != null) { + queryParameters["ordering"] = requestParameters["ordering"]; + } + + if (requestParameters["page"] != null) { + queryParameters["page"] = requestParameters["page"]; + } + + if (requestParameters["pageSize"] != null) { + queryParameters["page_size"] = requestParameters["pageSize"]; + } + + if (requestParameters["search"] != null) { + queryParameters["search"] = requestParameters["search"]; + } + + if (requestParameters["user"] != null) { + queryParameters["user"] = requestParameters["user"]; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/`; + + return { + path: urlPath, + method: "GET", + headers: headerParameters, + query: queryParameters, + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseListRaw( + requestParameters: EndpointsAgentsPssoIseListRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = await this.endpointsAgentsPssoIseListRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => + PaginatedAppleIndependentSecureEnclaveListFromJSON(jsonValue), + ); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseList( + requestParameters: EndpointsAgentsPssoIseListRequest = {}, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.endpointsAgentsPssoIseListRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Creates request options for endpointsAgentsPssoIsePartialUpdate without sending the request + */ + async endpointsAgentsPssoIsePartialUpdateRequestOpts( + requestParameters: EndpointsAgentsPssoIsePartialUpdateRequest, + ): Promise { + if (requestParameters["uuid"] == null) { + throw new runtime.RequiredError( + "uuid", + 'Required parameter "uuid" was null or undefined when calling endpointsAgentsPssoIsePartialUpdate().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters["Content-Type"] = "application/json"; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/{uuid}/`; + urlPath = urlPath.replace( + `{${"uuid"}}`, + encodeURIComponent(String(requestParameters["uuid"])), + ); + + return { + path: urlPath, + method: "PATCH", + headers: headerParameters, + query: queryParameters, + body: PatchedAppleIndependentSecureEnclaveRequestToJSON( + requestParameters["patchedAppleIndependentSecureEnclaveRequest"], + ), + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIsePartialUpdateRaw( + requestParameters: EndpointsAgentsPssoIsePartialUpdateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = + await this.endpointsAgentsPssoIsePartialUpdateRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => + AppleIndependentSecureEnclaveFromJSON(jsonValue), + ); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIsePartialUpdate( + requestParameters: EndpointsAgentsPssoIsePartialUpdateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.endpointsAgentsPssoIsePartialUpdateRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Creates request options for endpointsAgentsPssoIseRetrieve without sending the request + */ + async endpointsAgentsPssoIseRetrieveRequestOpts( + requestParameters: EndpointsAgentsPssoIseRetrieveRequest, + ): Promise { + if (requestParameters["uuid"] == null) { + throw new runtime.RequiredError( + "uuid", + 'Required parameter "uuid" was null or undefined when calling endpointsAgentsPssoIseRetrieve().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/{uuid}/`; + urlPath = urlPath.replace( + `{${"uuid"}}`, + encodeURIComponent(String(requestParameters["uuid"])), + ); + + return { + path: urlPath, + method: "GET", + headers: headerParameters, + query: queryParameters, + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseRetrieveRaw( + requestParameters: EndpointsAgentsPssoIseRetrieveRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = + await this.endpointsAgentsPssoIseRetrieveRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => + AppleIndependentSecureEnclaveFromJSON(jsonValue), + ); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseRetrieve( + requestParameters: EndpointsAgentsPssoIseRetrieveRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.endpointsAgentsPssoIseRetrieveRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Creates request options for endpointsAgentsPssoIseUpdate without sending the request + */ + async endpointsAgentsPssoIseUpdateRequestOpts( + requestParameters: EndpointsAgentsPssoIseUpdateRequest, + ): Promise { + if (requestParameters["uuid"] == null) { + throw new runtime.RequiredError( + "uuid", + 'Required parameter "uuid" was null or undefined when calling endpointsAgentsPssoIseUpdate().', + ); + } + + if (requestParameters["appleIndependentSecureEnclaveRequest"] == null) { + throw new runtime.RequiredError( + "appleIndependentSecureEnclaveRequest", + 'Required parameter "appleIndependentSecureEnclaveRequest" was null or undefined when calling endpointsAgentsPssoIseUpdate().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters["Content-Type"] = "application/json"; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/{uuid}/`; + urlPath = urlPath.replace( + `{${"uuid"}}`, + encodeURIComponent(String(requestParameters["uuid"])), + ); + + return { + path: urlPath, + method: "PUT", + headers: headerParameters, + query: queryParameters, + body: AppleIndependentSecureEnclaveRequestToJSON( + requestParameters["appleIndependentSecureEnclaveRequest"], + ), + }; + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseUpdateRaw( + requestParameters: EndpointsAgentsPssoIseUpdateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const requestOptions = + await this.endpointsAgentsPssoIseUpdateRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => + AppleIndependentSecureEnclaveFromJSON(jsonValue), + ); + } + + /** + * Mixin to add a used_by endpoint to return a list of all objects using this object + */ + async endpointsAgentsPssoIseUpdate( + requestParameters: EndpointsAgentsPssoIseUpdateRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.endpointsAgentsPssoIseUpdateRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Creates request options for endpointsAgentsPssoIseUsedByList without sending the request + */ + async endpointsAgentsPssoIseUsedByListRequestOpts( + requestParameters: EndpointsAgentsPssoIseUsedByListRequest, + ): Promise { + if (requestParameters["uuid"] == null) { + throw new runtime.RequiredError( + "uuid", + 'Required parameter "uuid" was null or undefined when calling endpointsAgentsPssoIseUsedByList().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("authentik", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/endpoints/agents/psso/ise/{uuid}/used_by/`; + urlPath = urlPath.replace( + `{${"uuid"}}`, + encodeURIComponent(String(requestParameters["uuid"])), + ); + + return { + path: urlPath, + method: "GET", + headers: headerParameters, + query: queryParameters, + }; + } + + /** + * Get a list of all objects that use this object + */ + async endpointsAgentsPssoIseUsedByListRaw( + requestParameters: EndpointsAgentsPssoIseUsedByListRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const requestOptions = + await this.endpointsAgentsPssoIseUsedByListRequestOpts(requestParameters); + const response = await this.request(requestOptions, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(UsedByFromJSON)); + } + + /** + * Get a list of all objects that use this object + */ + async endpointsAgentsPssoIseUsedByList( + requestParameters: EndpointsAgentsPssoIseUsedByListRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.endpointsAgentsPssoIseUsedByListRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + /** * Creates request options for endpointsAgentsPssoRegisterDeviceCreate without sending the request */ diff --git a/packages/client-ts/src/models/AppleIndependentSecureEnclave.ts b/packages/client-ts/src/models/AppleIndependentSecureEnclave.ts new file mode 100644 index 0000000000..d661b16663 --- /dev/null +++ b/packages/client-ts/src/models/AppleIndependentSecureEnclave.ts @@ -0,0 +1,106 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * authentik + * Making authentication simple. + * + * The version of the OpenAPI document: 2026.5.0-rc1 + * Contact: hello@goauthentik.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface AppleIndependentSecureEnclave + */ +export interface AppleIndependentSecureEnclave { + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclave + */ + uuid?: string; + /** + * The user that this device belongs to. + * @type {number} + * @memberof AppleIndependentSecureEnclave + */ + user: number; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclave + */ + appleSecureEnclaveKey: string; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclave + */ + appleEnclaveKeyId: string; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclave + */ + deviceType: string; +} + +/** + * Check if a given object implements the AppleIndependentSecureEnclave interface. + */ +export function instanceOfAppleIndependentSecureEnclave( + value: object, +): value is AppleIndependentSecureEnclave { + if (!("user" in value) || value["user"] === undefined) return false; + if (!("appleSecureEnclaveKey" in value) || value["appleSecureEnclaveKey"] === undefined) + return false; + if (!("appleEnclaveKeyId" in value) || value["appleEnclaveKeyId"] === undefined) return false; + if (!("deviceType" in value) || value["deviceType"] === undefined) return false; + return true; +} + +export function AppleIndependentSecureEnclaveFromJSON(json: any): AppleIndependentSecureEnclave { + return AppleIndependentSecureEnclaveFromJSONTyped(json, false); +} + +export function AppleIndependentSecureEnclaveFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): AppleIndependentSecureEnclave { + if (json == null) { + return json; + } + return { + uuid: json["uuid"] == null ? undefined : json["uuid"], + user: json["user"], + appleSecureEnclaveKey: json["apple_secure_enclave_key"], + appleEnclaveKeyId: json["apple_enclave_key_id"], + deviceType: json["device_type"], + }; +} + +export function AppleIndependentSecureEnclaveToJSON(json: any): AppleIndependentSecureEnclave { + return AppleIndependentSecureEnclaveToJSONTyped(json, false); +} + +export function AppleIndependentSecureEnclaveToJSONTyped( + value?: AppleIndependentSecureEnclave | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + uuid: value["uuid"], + user: value["user"], + apple_secure_enclave_key: value["appleSecureEnclaveKey"], + apple_enclave_key_id: value["appleEnclaveKeyId"], + device_type: value["deviceType"], + }; +} diff --git a/packages/client-ts/src/models/AppleIndependentSecureEnclaveRequest.ts b/packages/client-ts/src/models/AppleIndependentSecureEnclaveRequest.ts new file mode 100644 index 0000000000..a37808e2b0 --- /dev/null +++ b/packages/client-ts/src/models/AppleIndependentSecureEnclaveRequest.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * authentik + * Making authentication simple. + * + * The version of the OpenAPI document: 2026.5.0-rc1 + * Contact: hello@goauthentik.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface AppleIndependentSecureEnclaveRequest + */ +export interface AppleIndependentSecureEnclaveRequest { + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclaveRequest + */ + uuid?: string; + /** + * The user that this device belongs to. + * @type {number} + * @memberof AppleIndependentSecureEnclaveRequest + */ + user: number; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclaveRequest + */ + appleSecureEnclaveKey: string; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclaveRequest + */ + appleEnclaveKeyId: string; + /** + * + * @type {string} + * @memberof AppleIndependentSecureEnclaveRequest + */ + deviceType: string; +} + +/** + * Check if a given object implements the AppleIndependentSecureEnclaveRequest interface. + */ +export function instanceOfAppleIndependentSecureEnclaveRequest( + value: object, +): value is AppleIndependentSecureEnclaveRequest { + if (!("user" in value) || value["user"] === undefined) return false; + if (!("appleSecureEnclaveKey" in value) || value["appleSecureEnclaveKey"] === undefined) + return false; + if (!("appleEnclaveKeyId" in value) || value["appleEnclaveKeyId"] === undefined) return false; + if (!("deviceType" in value) || value["deviceType"] === undefined) return false; + return true; +} + +export function AppleIndependentSecureEnclaveRequestFromJSON( + json: any, +): AppleIndependentSecureEnclaveRequest { + return AppleIndependentSecureEnclaveRequestFromJSONTyped(json, false); +} + +export function AppleIndependentSecureEnclaveRequestFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): AppleIndependentSecureEnclaveRequest { + if (json == null) { + return json; + } + return { + uuid: json["uuid"] == null ? undefined : json["uuid"], + user: json["user"], + appleSecureEnclaveKey: json["apple_secure_enclave_key"], + appleEnclaveKeyId: json["apple_enclave_key_id"], + deviceType: json["device_type"], + }; +} + +export function AppleIndependentSecureEnclaveRequestToJSON( + json: any, +): AppleIndependentSecureEnclaveRequest { + return AppleIndependentSecureEnclaveRequestToJSONTyped(json, false); +} + +export function AppleIndependentSecureEnclaveRequestToJSONTyped( + value?: AppleIndependentSecureEnclaveRequest | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + uuid: value["uuid"], + user: value["user"], + apple_secure_enclave_key: value["appleSecureEnclaveKey"], + apple_enclave_key_id: value["appleEnclaveKeyId"], + device_type: value["deviceType"], + }; +} diff --git a/packages/client-ts/src/models/AuthenticationEnum.ts b/packages/client-ts/src/models/AuthenticationEnum.ts index babf1fb0c9..fba12d4fcd 100644 --- a/packages/client-ts/src/models/AuthenticationEnum.ts +++ b/packages/client-ts/src/models/AuthenticationEnum.ts @@ -23,6 +23,7 @@ export const AuthenticationEnum = { RequireSuperuser: "require_superuser", RequireRedirect: "require_redirect", RequireOutpost: "require_outpost", + RequireToken: "require_token", UnknownDefaultOpenApi: "11184809", } as const; export type AuthenticationEnum = (typeof AuthenticationEnum)[keyof typeof AuthenticationEnum]; diff --git a/packages/client-ts/src/models/PaginatedAppleIndependentSecureEnclaveList.ts b/packages/client-ts/src/models/PaginatedAppleIndependentSecureEnclaveList.ts new file mode 100644 index 0000000000..c53d89bc21 --- /dev/null +++ b/packages/client-ts/src/models/PaginatedAppleIndependentSecureEnclaveList.ts @@ -0,0 +1,100 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * authentik + * Making authentication simple. + * + * The version of the OpenAPI document: 2026.5.0-rc1 + * Contact: hello@goauthentik.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { AppleIndependentSecureEnclave } from "./AppleIndependentSecureEnclave"; +import { + AppleIndependentSecureEnclaveFromJSON, + AppleIndependentSecureEnclaveToJSON, +} from "./AppleIndependentSecureEnclave"; +import type { Pagination } from "./Pagination"; +import { PaginationFromJSON, PaginationToJSON } from "./Pagination"; + +/** + * + * @export + * @interface PaginatedAppleIndependentSecureEnclaveList + */ +export interface PaginatedAppleIndependentSecureEnclaveList { + /** + * + * @type {Pagination} + * @memberof PaginatedAppleIndependentSecureEnclaveList + */ + pagination: Pagination; + /** + * + * @type {Array} + * @memberof PaginatedAppleIndependentSecureEnclaveList + */ + results: Array; + /** + * + * @type {{ [key: string]: any; }} + * @memberof PaginatedAppleIndependentSecureEnclaveList + */ + autocomplete: { [key: string]: any }; +} + +/** + * Check if a given object implements the PaginatedAppleIndependentSecureEnclaveList interface. + */ +export function instanceOfPaginatedAppleIndependentSecureEnclaveList( + value: object, +): value is PaginatedAppleIndependentSecureEnclaveList { + if (!("pagination" in value) || value["pagination"] === undefined) return false; + if (!("results" in value) || value["results"] === undefined) return false; + if (!("autocomplete" in value) || value["autocomplete"] === undefined) return false; + return true; +} + +export function PaginatedAppleIndependentSecureEnclaveListFromJSON( + json: any, +): PaginatedAppleIndependentSecureEnclaveList { + return PaginatedAppleIndependentSecureEnclaveListFromJSONTyped(json, false); +} + +export function PaginatedAppleIndependentSecureEnclaveListFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): PaginatedAppleIndependentSecureEnclaveList { + if (json == null) { + return json; + } + return { + pagination: PaginationFromJSON(json["pagination"]), + results: (json["results"] as Array).map(AppleIndependentSecureEnclaveFromJSON), + autocomplete: json["autocomplete"], + }; +} + +export function PaginatedAppleIndependentSecureEnclaveListToJSON( + json: any, +): PaginatedAppleIndependentSecureEnclaveList { + return PaginatedAppleIndependentSecureEnclaveListToJSONTyped(json, false); +} + +export function PaginatedAppleIndependentSecureEnclaveListToJSONTyped( + value?: PaginatedAppleIndependentSecureEnclaveList | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + pagination: PaginationToJSON(value["pagination"]), + results: (value["results"] as Array).map(AppleIndependentSecureEnclaveToJSON), + autocomplete: value["autocomplete"], + }; +} diff --git a/packages/client-ts/src/models/PatchedAppleIndependentSecureEnclaveRequest.ts b/packages/client-ts/src/models/PatchedAppleIndependentSecureEnclaveRequest.ts new file mode 100644 index 0000000000..a35f6ff79a --- /dev/null +++ b/packages/client-ts/src/models/PatchedAppleIndependentSecureEnclaveRequest.ts @@ -0,0 +1,107 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * authentik + * Making authentication simple. + * + * The version of the OpenAPI document: 2026.5.0-rc1 + * Contact: hello@goauthentik.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface PatchedAppleIndependentSecureEnclaveRequest + */ +export interface PatchedAppleIndependentSecureEnclaveRequest { + /** + * + * @type {string} + * @memberof PatchedAppleIndependentSecureEnclaveRequest + */ + uuid?: string; + /** + * The user that this device belongs to. + * @type {number} + * @memberof PatchedAppleIndependentSecureEnclaveRequest + */ + user?: number; + /** + * + * @type {string} + * @memberof PatchedAppleIndependentSecureEnclaveRequest + */ + appleSecureEnclaveKey?: string; + /** + * + * @type {string} + * @memberof PatchedAppleIndependentSecureEnclaveRequest + */ + appleEnclaveKeyId?: string; + /** + * + * @type {string} + * @memberof PatchedAppleIndependentSecureEnclaveRequest + */ + deviceType?: string; +} + +/** + * Check if a given object implements the PatchedAppleIndependentSecureEnclaveRequest interface. + */ +export function instanceOfPatchedAppleIndependentSecureEnclaveRequest( + value: object, +): value is PatchedAppleIndependentSecureEnclaveRequest { + return true; +} + +export function PatchedAppleIndependentSecureEnclaveRequestFromJSON( + json: any, +): PatchedAppleIndependentSecureEnclaveRequest { + return PatchedAppleIndependentSecureEnclaveRequestFromJSONTyped(json, false); +} + +export function PatchedAppleIndependentSecureEnclaveRequestFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): PatchedAppleIndependentSecureEnclaveRequest { + if (json == null) { + return json; + } + return { + uuid: json["uuid"] == null ? undefined : json["uuid"], + user: json["user"] == null ? undefined : json["user"], + appleSecureEnclaveKey: + json["apple_secure_enclave_key"] == null ? undefined : json["apple_secure_enclave_key"], + appleEnclaveKeyId: + json["apple_enclave_key_id"] == null ? undefined : json["apple_enclave_key_id"], + deviceType: json["device_type"] == null ? undefined : json["device_type"], + }; +} + +export function PatchedAppleIndependentSecureEnclaveRequestToJSON( + json: any, +): PatchedAppleIndependentSecureEnclaveRequest { + return PatchedAppleIndependentSecureEnclaveRequestToJSONTyped(json, false); +} + +export function PatchedAppleIndependentSecureEnclaveRequestToJSONTyped( + value?: PatchedAppleIndependentSecureEnclaveRequest | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + uuid: value["uuid"], + user: value["user"], + apple_secure_enclave_key: value["appleSecureEnclaveKey"], + apple_enclave_key_id: value["appleEnclaveKeyId"], + device_type: value["deviceType"], + }; +} diff --git a/packages/client-ts/src/models/index.ts b/packages/client-ts/src/models/index.ts index e24455ffaf..de3ce2683c 100644 --- a/packages/client-ts/src/models/index.ts +++ b/packages/client-ts/src/models/index.ts @@ -13,6 +13,8 @@ export * from "./AlgEnum"; export * from "./App"; export * from "./AppEnum"; export * from "./AppleChallengeResponseRequest"; +export * from "./AppleIndependentSecureEnclave"; +export * from "./AppleIndependentSecureEnclaveRequest"; export * from "./AppleLoginChallenge"; export * from "./Application"; export * from "./ApplicationEntitlement"; @@ -350,6 +352,7 @@ export * from "./OutpostRequest"; export * from "./OutpostTypeEnum"; export * from "./PKCEMethodEnum"; export * from "./PaginatedAgentConnectorList"; +export * from "./PaginatedAppleIndependentSecureEnclaveList"; export * from "./PaginatedApplicationEntitlementList"; export * from "./PaginatedApplicationList"; export * from "./PaginatedAuthenticatedSessionList"; @@ -515,6 +518,7 @@ export * from "./PasswordPolicyRequest"; export * from "./PasswordStage"; export * from "./PasswordStageRequest"; export * from "./PatchedAgentConnectorRequest"; +export * from "./PatchedAppleIndependentSecureEnclaveRequest"; export * from "./PatchedApplicationEntitlementRequest"; export * from "./PatchedApplicationRequest"; export * from "./PatchedAuthenticatorDuoStageRequest"; diff --git a/pyproject.toml b/pyproject.toml index 38968ee6c7..477f670bf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "drf-spectacular==0.29.0", "dumb-init==1.2.5.post1", "duo-client==5.6.1", - "fido2==2.1.1", + "fido2==2.2.0", "geoip2==5.2.0", "geopy==2.4.1", "google-api-python-client==2.194.0", @@ -43,26 +43,26 @@ dependencies = [ "jwcrypto==1.5.7", "kubernetes==35.0.0", "ldap3==2.9.1", - "lxml==6.0.4", + "lxml==6.1.0", "msgraph-sdk==1.55.0", "opencontainers==0.0.15", - "packaging==26.0", + "packaging==26.1", "paramiko==4.0.0", "psycopg[c,pool]==3.3.3", "pydantic-scim==0.0.8", - "pydantic==2.12.5", + "pydantic==2.13.2", "pyjwt==2.11.0", "pyrad==2.5.4", "python-kadmin-rs==0.7.0", "pyyaml==6.0.3", "requests-oauthlib==2.0.0", "scim2-filter-parser==0.7.0", - "sentry-sdk==2.57.0", + "sentry-sdk==2.58.0", "service-identity==24.2.0", "setproctitle==1.3.7", "structlog==25.5.0", "swagger-spec-validator==3.0.4", - "twilio==9.10.4", + "twilio==9.10.5", "ua-parser==1.0.2", "unidecode==1.4.0", "urllib3<3", @@ -76,7 +76,7 @@ dependencies = [ [dependency-groups] dev = [ - "aws-cdk-lib==2.248.0", + "aws-cdk-lib==2.250.0", "bandit==1.9.4", "black==26.3.1", "bpython==0.26", @@ -85,7 +85,7 @@ dev = [ "coverage[toml]==7.13.5", "daphne==4.2.1", "debugpy==1.8.20", - "django-stubs[compatible-mypy]==6.0.2", + "django-stubs[compatible-mypy]==6.0.3", "djangorestframework-stubs[compatible-mypy]==3.16.9", "drf-jsonschema-serializer==3.0.0", "freezegun==1.5.5", @@ -101,8 +101,8 @@ dev = [ "pytest-timeout==2.4.0", "pytest==9.0.3", "requests-mock==1.12.1", - "ruff==0.15.10", - "selenium==4.41.0", + "ruff==0.15.11", + "selenium==4.43.0", "types-channels==4.3.0.20260408", "types-docker==7.1.0.20260409", "types-jwcrypto==1.5.7.20260409", diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 834156724d..113908ff13 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.94.1" +channel = "1.95.0" components = ["clippy", "rust-analyzer", "llvm-tools-preview"] diff --git a/schema.yml b/schema.yml index f5f27ba053..a746ee831c 100644 --- a/schema.yml +++ b/schema.yml @@ -5649,6 +5649,209 @@ paths: $ref: '#/components/responses/ValidationErrorResponse' '403': $ref: '#/components/responses/GenericErrorResponse' + /endpoints/agents/psso/ise/: + get: + operationId: endpoints_agents_psso_ise_list + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + parameters: + - in: query + name: apple_enclave_key_id + schema: + type: string + - $ref: '#/components/parameters/QueryPaginationOrdering' + - $ref: '#/components/parameters/QueryPaginationPage' + - $ref: '#/components/parameters/QueryPaginationPageSize' + - $ref: '#/components/parameters/QuerySearch' + - in: query + name: user + schema: + type: integer + tags: + - endpoints + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedAppleIndependentSecureEnclaveList' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + post: + operationId: endpoints_agents_psso_ise_create + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + tags: + - endpoints + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclaveRequest' + required: true + security: + - authentik: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclave' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + /endpoints/agents/psso/ise/{uuid}/: + get: + operationId: endpoints_agents_psso_ise_retrieve + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Apple Independent Secure Enclave. + required: true + tags: + - endpoints + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclave' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + put: + operationId: endpoints_agents_psso_ise_update + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Apple Independent Secure Enclave. + required: true + tags: + - endpoints + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclaveRequest' + required: true + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclave' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + patch: + operationId: endpoints_agents_psso_ise_partial_update + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Apple Independent Secure Enclave. + required: true + tags: + - endpoints + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedAppleIndependentSecureEnclaveRequest' + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AppleIndependentSecureEnclave' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + delete: + operationId: endpoints_agents_psso_ise_destroy + description: Mixin to add a used_by endpoint to return a list of all objects + using this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Apple Independent Secure Enclave. + required: true + tags: + - endpoints + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' + /endpoints/agents/psso/ise/{uuid}/used_by/: + get: + operationId: endpoints_agents_psso_ise_used_by_list + description: Get a list of all objects that use this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Apple Independent Secure Enclave. + required: true + tags: + - endpoints + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UsedBy' + description: '' + '400': + $ref: '#/components/responses/ValidationErrorResponse' + '403': + $ref: '#/components/responses/GenericErrorResponse' /endpoints/agents/psso/register/device/: post: operationId: endpoints_agents_psso_register_device_create @@ -33772,6 +33975,49 @@ components: type: string minLength: 1 default: ak-source-oauth-apple + AppleIndependentSecureEnclave: + type: object + properties: + uuid: + type: string + format: uuid + user: + type: integer + description: The user that this device belongs to. + apple_secure_enclave_key: + type: string + apple_enclave_key_id: + type: string + device_type: + type: string + required: + - apple_enclave_key_id + - apple_secure_enclave_key + - device_type + - user + AppleIndependentSecureEnclaveRequest: + type: object + properties: + uuid: + type: string + format: uuid + user: + type: integer + description: The user that this device belongs to. + apple_secure_enclave_key: + type: string + minLength: 1 + apple_enclave_key_id: + type: string + minLength: 1 + device_type: + type: string + minLength: 1 + required: + - apple_enclave_key_id + - apple_secure_enclave_key + - device_type + - user AppleLoginChallenge: type: object description: Special challenge for apple-native authentication flow, which happens @@ -34109,6 +34355,7 @@ components: - require_superuser - require_redirect - require_outpost + - require_token type: string AuthenticatorAttachmentEnum: enum: @@ -44295,6 +44542,21 @@ components: - autocomplete - pagination - results + PaginatedAppleIndependentSecureEnclaveList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/AppleIndependentSecureEnclave' + autocomplete: + $ref: '#/components/schemas/Autocomplete' + required: + - autocomplete + - pagination + - results PaginatedApplicationEntitlementList: type: object properties: @@ -47080,6 +47342,24 @@ components: type: array items: type: integer + PatchedAppleIndependentSecureEnclaveRequest: + type: object + properties: + uuid: + type: string + format: uuid + user: + type: integer + description: The user that this device belongs to. + apple_secure_enclave_key: + type: string + minLength: 1 + apple_enclave_key_id: + type: string + minLength: 1 + device_type: + type: string + minLength: 1 PatchedApplicationEntitlementRequest: type: object description: ApplicationEntitlement Serializer diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index 7fdf43b438..9315c1c938 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -74,7 +74,7 @@ pub(crate) fn start(tasks: &mut Tasks) -> Result> { tasks .build_task() - .name(&format!("{}::run_upkeep", module_path!(),)) + .name(&format!("{}::run_upkeep", module_path!())) .spawn(run_upkeep(arbiter, Arc::clone(&metrics)))?; for addr in config::get().listen.metrics.iter().copied() { diff --git a/src/worker/mod.rs b/src/worker/mod.rs index a077acc123..c852fba212 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -70,7 +70,7 @@ impl Worker { cmd.arg(&socket_path); let client = Client::builder(TokioExecutor::new()) - .pool_idle_timeout(Duration::from_secs(60)) + .pool_idle_timeout(Duration::from_mins(1)) .set_host(false) .build(UnixSocketConnector::new(socket_path.clone())); diff --git a/uv.lock b/uv.lock index 52f66dee31..60a5dc6547 100644 --- a/uv.lock +++ b/uv.lock @@ -342,7 +342,7 @@ requires-dist = [ { name = "drf-spectacular", specifier = "==0.29.0" }, { name = "dumb-init", specifier = "==1.2.5.post1" }, { name = "duo-client", specifier = "==5.6.1" }, - { name = "fido2", specifier = "==2.1.1" }, + { name = "fido2", specifier = "==2.2.0" }, { name = "geoip2", specifier = "==5.2.0" }, { name = "geopy", specifier = "==2.4.1" }, { name = "google-api-python-client", specifier = "==2.194.0" }, @@ -352,13 +352,13 @@ requires-dist = [ { name = "jwcrypto", specifier = "==1.5.7" }, { name = "kubernetes", specifier = "==35.0.0" }, { name = "ldap3", specifier = "==2.9.1" }, - { name = "lxml", specifier = "==6.0.4" }, + { name = "lxml", specifier = "==6.1.0" }, { name = "msgraph-sdk", specifier = "==1.55.0" }, { name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" }, - { name = "packaging", specifier = "==26.0" }, + { name = "packaging", specifier = "==26.1" }, { name = "paramiko", specifier = "==4.0.0" }, { name = "psycopg", extras = ["c", "pool"], specifier = "==3.3.3" }, - { name = "pydantic", specifier = "==2.12.5" }, + { name = "pydantic", specifier = "==2.13.2" }, { name = "pydantic-scim", specifier = "==0.0.8" }, { name = "pyjwt", specifier = "==2.11.0" }, { name = "pyrad", specifier = "==2.5.4" }, @@ -366,12 +366,12 @@ requires-dist = [ { name = "pyyaml", specifier = "==6.0.3" }, { name = "requests-oauthlib", specifier = "==2.0.0" }, { name = "scim2-filter-parser", specifier = "==0.7.0" }, - { name = "sentry-sdk", specifier = "==2.57.0" }, + { name = "sentry-sdk", specifier = "==2.58.0" }, { name = "service-identity", specifier = "==24.2.0" }, { name = "setproctitle", specifier = "==1.3.7" }, { name = "structlog", specifier = "==25.5.0" }, { name = "swagger-spec-validator", specifier = "==3.0.4" }, - { name = "twilio", specifier = "==9.10.4" }, + { name = "twilio", specifier = "==9.10.5" }, { name = "ua-parser", specifier = "==1.0.2" }, { name = "unidecode", specifier = "==1.4.0" }, { name = "urllib3", specifier = "<3" }, @@ -385,7 +385,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "aws-cdk-lib", specifier = "==2.248.0" }, + { name = "aws-cdk-lib", specifier = "==2.250.0" }, { name = "bandit", specifier = "==1.9.4" }, { name = "black", specifier = "==26.3.1" }, { name = "bpython", specifier = "==0.26" }, @@ -394,7 +394,7 @@ dev = [ { name = "coverage", extras = ["toml"], specifier = "==7.13.5" }, { name = "daphne", specifier = "==4.2.1" }, { name = "debugpy", specifier = "==1.8.20" }, - { name = "django-stubs", extras = ["compatible-mypy"], specifier = "==6.0.2" }, + { name = "django-stubs", extras = ["compatible-mypy"], specifier = "==6.0.3" }, { name = "djangorestframework-stubs", extras = ["compatible-mypy"], specifier = "==3.16.9" }, { name = "drf-jsonschema-serializer", specifier = "==3.0.0" }, { name = "freezegun", specifier = "==1.5.5" }, @@ -410,8 +410,8 @@ dev = [ { name = "pytest-randomly", specifier = "==4.0.1" }, { name = "pytest-timeout", specifier = "==2.4.0" }, { name = "requests-mock", specifier = "==1.12.1" }, - { name = "ruff", specifier = "==0.15.10" }, - { name = "selenium", specifier = "==4.41.0" }, + { name = "ruff", specifier = "==0.15.11" }, + { name = "selenium", specifier = "==4.43.0" }, { name = "types-channels", specifier = "==4.3.0.20260408" }, { name = "types-docker", specifier = "==7.1.0.20260409" }, { name = "types-jwcrypto", specifier = "==1.5.7.20260409" }, @@ -495,7 +495,7 @@ wheels = [ [[package]] name = "aws-cdk-lib" -version = "2.248.0" +version = "2.250.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-cdk-asset-awscli-v1" }, @@ -506,9 +506,9 @@ dependencies = [ { name = "publication" }, { name = "typeguard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/42/a8250f31763adb21acb90088b99f7f37c3b8d09c3fd94dc631c0e69d9463/aws_cdk_lib-2.248.0.tar.gz", hash = "sha256:a00c73bfa6865ef62d700d6f97dd4124979ccfe69dc1ef70f2526aa49c52bbc8", size = 48734274, upload-time = "2026-04-03T07:35:05.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/7b/98c66b6ea6d4f84b70d86ec391bbcd2856a82b384a97adc223f36b36dfd6/aws_cdk_lib-2.250.0.tar.gz", hash = "sha256:6e5cb8def9208a45cede1376a81d7508b3889879ccc7e9cddaa4fd807da0b144", size = 49146123, upload-time = "2026-04-14T21:43:07.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/0b/41b0979581668c709b01a7d26f9c2716031f2b0c53ad5a01341160c631b0/aws_cdk_lib-2.248.0-py3-none-any.whl", hash = "sha256:e89db288365371e6b42aaae8b45ad2064615dfbe87e95741a2f2bb7ec1f33dad", size = 49395398, upload-time = "2026-04-03T07:34:25.671Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e7/b64389b59215a42d0d7538a1d9a8006edb2eefb297ccbdcd4c55ff2cffba/aws_cdk_lib-2.250.0-py3-none-any.whl", hash = "sha256:427c9a062f350c16e301326fd6ca0440428f9cc0e85421aaa69a9afa57228acc", size = 49827287, upload-time = "2026-04-14T21:42:21.21Z" }, ] [[package]] @@ -1253,7 +1253,7 @@ s3 = [ [[package]] name = "django-stubs" -version = "6.0.2" +version = "6.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django" }, @@ -1261,9 +1261,9 @@ dependencies = [ { name = "types-pyyaml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/b2/f0214d86180f937c8e3358ff831b20f0634d95bd77436b18861c647e15bc/django_stubs-6.0.2.tar.gz", hash = "sha256:56d43b5e3741563af0063e5b6283f908c625b0439aa06314268673699d1bdccd", size = 274742, upload-time = "2026-04-01T08:27:35.092Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/0c/8d0d875af79bf774c1c3997c84aa118dba3a77be12086b9c14e130e8ec72/django_stubs-6.0.3.tar.gz", hash = "sha256:ee895f403c373608eeb50822f0733f9d9ec5ab12731d4ab58956053bb95fdd9e", size = 278214, upload-time = "2026-04-18T15:11:22.327Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/e7/8f2aaa22eac7fa18db3aca0e7b651ccf5ac79a2021bf67e75a16934a7076/django_stubs-6.0.2-py3-none-any.whl", hash = "sha256:c3bc84d80421758f3b2ad9e1358e001d719388a8eb106e67c873e606216108d4", size = 538234, upload-time = "2026-04-01T08:27:33.411Z" }, + { url = "https://files.pythonhosted.org/packages/80/a3/6751b7684d20fc4f228bdd3dd8341d382ab3faaf65d3d050c0d59ab0a1b0/django_stubs-6.0.3-py3-none-any.whl", hash = "sha256:5fee22bcbbad59a78c727a820b6f4e68ff442ca76a922b7002e57c25dd7cb390", size = 541570, upload-time = "2026-04-18T15:11:20.711Z" }, ] [package.optional-dependencies] @@ -1468,14 +1468,14 @@ wheels = [ [[package]] name = "fido2" -version = "2.1.1" +version = "2.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/3c/c65377e48c144afca6b02c69f10c0fe936db556096a4e2c9798e2aa72db6/fido2-2.1.1.tar.gz", hash = "sha256:f1379f845870cc7fc64c7f07323c3ce41e8c96c37054e79e0acd5630b3fec5ac", size = 4455940, upload-time = "2026-01-19T11:08:34.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/34/4837e2f5640baf61d8abd6125ccb6cc60b4b2933088528356ad6e781496f/fido2-2.2.0.tar.gz", hash = "sha256:0d8122e690096ad82afde42ac9d6433a4eeffda64084f36341ea02546b181dd1", size = 294167, upload-time = "2026-04-15T06:42:50.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/ab/d0fa89cc4b982800dd88daa799612f11642bf9393851715d9eaeba3cfcac/fido2-2.1.1-py3-none-any.whl", hash = "sha256:f85c16c8084abf6530b6c6ec3a0cf8575943321842e06916686943a8b784182c", size = 226945, upload-time = "2026-01-19T11:08:29.675Z" }, + { url = "https://files.pythonhosted.org/packages/01/82/f3c5dd87b0977f5547cc132b7969e6f5075a8c2f5881cf4b6df6378505f9/fido2-2.2.0-py3-none-any.whl", hash = "sha256:3587ccf0af7b71b5dd73f17e1dbec9f0fd157292f9163f02e7778f46d0d25fe5", size = 234025, upload-time = "2026-04-15T06:42:51.813Z" }, ] [[package]] @@ -2060,46 +2060,46 @@ wheels = [ [[package]] name = "lxml" -version = "6.0.4" +version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/08/1217ca4043f55c3c92993b283a7dbfa456a2058d8b57bbb416cc96b6efff/lxml-6.0.4.tar.gz", hash = "sha256:4137516be2a90775f99d8ef80ec0283f8d78b5d8bd4630ff20163b72e7e9abf2", size = 4237780, upload-time = "2026-04-12T16:28:24.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/30/9abc9e34c657c33834eaf6cd02124c61bdf5944d802aa48e69be8da3585d/lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13", size = 4197006, upload-time = "2026-04-18T04:32:51.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/18/36e28a809c509a67496202771f545219ac5a2f1cd61aae325991fcf5ab91/lxml-6.0.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:569d3b18340863f603582d2124e742a68e85755eff5e47c26a55e298521e3a01", size = 8575045, upload-time = "2026-04-12T16:25:33.57Z" }, - { url = "https://files.pythonhosted.org/packages/11/38/a168c820e3b08d3b4fa0f4e6b53b3930086b36cc11e428106d38c36778cd/lxml-6.0.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b6245ee5241342d45e1a54a4a8bc52ef322333ada74f24aa335c4ab36f20161", size = 4622963, upload-time = "2026-04-12T16:25:36.818Z" }, - { url = "https://files.pythonhosted.org/packages/53/e0/2c9d6abdd82358cea3c0d8d6ca272a6af0f38156abce7827efb6d5b62d17/lxml-6.0.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79a1173ba3213a3693889a435417d4e9f3c07d96e30dc7cc3a712ed7361015fe", size = 4948832, upload-time = "2026-04-12T16:25:39.104Z" }, - { url = "https://files.pythonhosted.org/packages/96/d7/f2202852e91d7baf3a317f4523a9c14834145301e5b0f2e80c01c4bfbd49/lxml-6.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc18bb975666b443ba23aedd2fcf57e9d0d97546b52a1de97a447c4061ba4110", size = 5085865, upload-time = "2026-04-12T16:25:41.226Z" }, - { url = "https://files.pythonhosted.org/packages/09/57/abee549324496e92708f71391c6060a164d3c95369656a1a15e9f20d8162/lxml-6.0.4-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2079f5dc83291ac190a52f8354b78648f221ecac19fb2972a2d056b555824de7", size = 5030001, upload-time = "2026-04-12T16:25:43.695Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/432da7178c5917a16468af6c5da68fef7cf3357d4bd0e6f50272ec9a59b5/lxml-6.0.4-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3eda02da4ca16e9ca22bbe5654470c17fa1abcd967a52e4c2e50ff278221e351", size = 5646303, upload-time = "2026-04-12T16:25:46.577Z" }, - { url = "https://files.pythonhosted.org/packages/82/f9/e1c04ef667a6bf9c9dbd3bf04c50fa51d7ee25b258485bb748b27eb9a1c7/lxml-6.0.4-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3787cdc3832b70e21ac2efafea2a82a8ccb5e85bec110dc68b26023e9d3caae", size = 5237940, upload-time = "2026-04-12T16:25:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/d0/f0/cdea60d92df731725fc3c4f33e387b100f210acd45c92969e42d2ba993fa/lxml-6.0.4-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:3f276d49c23103565d39440b9b3f4fc08fa22f5a96395ea4b4d4fea4458b1505", size = 5350050, upload-time = "2026-04-12T16:25:52.027Z" }, - { url = "https://files.pythonhosted.org/packages/2e/15/bf52c7a70b6081bb9e00d37cc90fcf60aa84468d9d173ad2fade38ec34c5/lxml-6.0.4-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:fdfdad73736402375b11b3a137e48cd09634177516baf5fc0bd80d1ca85f3cda", size = 4696409, upload-time = "2026-04-12T16:25:55.141Z" }, - { url = "https://files.pythonhosted.org/packages/c5/69/9bade267332cc06f9a9aa773b5a11bdfb249af485df9e142993009ea1fc4/lxml-6.0.4-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75912421456946931daba0ec3cedfa824c756585d05bde97813a17992bfbd013", size = 5249072, upload-time = "2026-04-12T16:25:57.362Z" }, - { url = "https://files.pythonhosted.org/packages/14/ca/043bcacb096d6ed291cbbc58724e9625a453069d6edeb840b0bf18038d05/lxml-6.0.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:48cd5a88da67233fd82f2920db344503c2818255217cd6ea462c9bb8254ba7cb", size = 5083779, upload-time = "2026-04-12T16:26:00.018Z" }, - { url = "https://files.pythonhosted.org/packages/04/89/f5fb18d76985969e84af13682e489acabee399bb54738a363925ea6e7390/lxml-6.0.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:87af86a8fa55b9ff1e6ee4233d762296f2ce641ba948af783fb995c5a8a3371b", size = 4736953, upload-time = "2026-04-12T16:26:02.289Z" }, - { url = "https://files.pythonhosted.org/packages/84/ba/d1d7284bb4ba951f188c3fc0455943c1fcbd1c33d1324d6d57b7d4a45be6/lxml-6.0.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a743714cd656ba7ccb29d199783906064c7b5ba3c0e2a79f0244ea0badc6a98c", size = 5669605, upload-time = "2026-04-12T16:26:04.694Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/1463e55f2de27bb60feddc894dd7c0833bd501f8861392ed416291b38db5/lxml-6.0.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e31c76bd066fb4f81d9a32e5843bffdf939ab27afb1ffc1c924e749bfbdb00e3", size = 5236886, upload-time = "2026-04-12T16:26:07.659Z" }, - { url = "https://files.pythonhosted.org/packages/fe/fb/0b6ee9194ce3ac49db4cadaa8a9158f04779fc768b6c27c4e2945d71a99d/lxml-6.0.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f185fd6e7d550e9917d7103dccf51be589aba953e15994fb04646c1730019685", size = 5263382, upload-time = "2026-04-12T16:26:10.067Z" }, - { url = "https://files.pythonhosted.org/packages/9a/93/ec18a08e98dd82cac39f1d2511ee2bed5affb94d228356d8ef165a4ec3b9/lxml-6.0.4-cp314-cp314-win32.whl", hash = "sha256:774660028f8722a598400430d2746fb0075949f84a9a5cd9767d9152e3baaac5", size = 3656164, upload-time = "2026-04-12T16:26:59.568Z" }, - { url = "https://files.pythonhosted.org/packages/15/86/52507316abfc7150bf6bb191e39a12e301ee80334610a493884ae2f9d20d/lxml-6.0.4-cp314-cp314-win_amd64.whl", hash = "sha256:fbd7d14349413f5609c0b537b1a48117d6ccef1af37986af6b03766ad05bf43e", size = 4062512, upload-time = "2026-04-12T16:27:02.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d5/09c593a2ef2234b8cd6cf059e2dc212e0654bf05c503f0ef2daf05adb680/lxml-6.0.4-cp314-cp314-win_arm64.whl", hash = "sha256:a61a01ec3fbfd5b73a69a7bf513271051fd6c5795d82fc5daa0255934cd8db3d", size = 3740745, upload-time = "2026-04-12T16:27:04.444Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3c/42a98bf6693938bf7b285ec7f70ba2ae9d785d0e5b2cdb85d2ee29e287eb/lxml-6.0.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:504edb62df33cea502ea6e73847c647ba228623ca3f80a228be5723a70984dd5", size = 8826437, upload-time = "2026-04-12T16:26:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c2/ad13f39b2db8709788aa2dcb6e90b81da76db3b5b2e7d35e0946cf984960/lxml-6.0.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f01b7b0316d4c0926d49a7f003b2d30539f392b140a3374bb788bad180bc8478", size = 4734892, upload-time = "2026-04-12T16:26:15.871Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6d/c559d7b5922c5b0380fc2cb5ac134b6a3f9d79d368347a624ee5d68b0816/lxml-6.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ab999933e662501efe4b16e6cfb7c9f9deca7d072cd1788b99c8defde78c0dfb", size = 4969173, upload-time = "2026-04-12T16:26:18.335Z" }, - { url = "https://files.pythonhosted.org/packages/c7/78/ca521e36157f38e3e1a29276855cdf48d213138fc0c8365693ff5c876ca7/lxml-6.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67c3f084389fe75932c39b6869a377f6c8e21e818f31ae8a30c71dd2e59360e2", size = 5103134, upload-time = "2026-04-12T16:26:20.612Z" }, - { url = "https://files.pythonhosted.org/packages/28/a7/7d62d023bacaa0aaf60af8c0a77c6c05f84327396d755f3aa64b788678a9/lxml-6.0.4-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:377ea1d654f76ed6205c87d14920f829c9f4d31df83374d3cbcbdaae804d37b2", size = 5027205, upload-time = "2026-04-12T16:26:22.981Z" }, - { url = "https://files.pythonhosted.org/packages/34/be/51b194b81684f2e85e5d992771c45d70cb22ac6f7291ac6bc7b255830afe/lxml-6.0.4-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e60cd0bcacbfd1a96d63516b622183fb2e3f202300df9eb5533391a8a939dbfa", size = 5594461, upload-time = "2026-04-12T16:26:25.316Z" }, - { url = "https://files.pythonhosted.org/packages/39/24/8850f38fbf89dd072ff31ba22f9e40347aeada7cadf710ecb04b8d9f32d4/lxml-6.0.4-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e9e30fd63d41dd0bbdb020af5cdfffd5d9b554d907cb210f18e8fcdc8eac013", size = 5223378, upload-time = "2026-04-12T16:26:28.68Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9b/595239ba8c719b0fdc7bc9ebdb7564459c9a6b24b8b363df4a02674aeece/lxml-6.0.4-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:1fb4a1606bb68c533002e7ed50d7e55e58f0ef1696330670281cb79d5ab2050d", size = 5311415, upload-time = "2026-04-12T16:26:31.513Z" }, - { url = "https://files.pythonhosted.org/packages/be/cb/aa27ac8d041acf34691577838494ad08df78e83fdfdb66948d2903e9291e/lxml-6.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:695c7708438e449d57f404db8cc1b769e77ad5b50655f32f8175686ba752f293", size = 4637953, upload-time = "2026-04-12T16:26:33.806Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f2/f19114fd86825c2d1ce41cd99daad218d30cfdd2093d4de9273986fb4d68/lxml-6.0.4-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d49c35ae1e35ee9b569892cf8f8f88db9524f28d66e9daee547a5ef9f3c5f468", size = 5231532, upload-time = "2026-04-12T16:26:36.518Z" }, - { url = "https://files.pythonhosted.org/packages/9a/0e/c3fa354039ec0b6b09f40fbe1129efc572ac6239faa4906de42d5ce87c0a/lxml-6.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5801072f8967625e6249d162065d0d6011ef8ce3d0efb8754496b5246b81a74b", size = 5083767, upload-time = "2026-04-12T16:26:39.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/4b/1a0dbb6d6ffae16e54a8a3796ded0ad2f9c3bc1ff3728bde33456f4e1d63/lxml-6.0.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cbf768541526eba5ef1a49f991122e41b39781eafd0445a5a110fc09947a20b5", size = 4758079, upload-time = "2026-04-12T16:26:42.138Z" }, - { url = "https://files.pythonhosted.org/packages/a9/01/a246cf5f80f96766051de4b305d6552f80bdaefb37f04e019e42af0aba69/lxml-6.0.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eecce87cc09233786fc31c230268183bf6375126cfec1c8b3673fcdc8767b560", size = 5618686, upload-time = "2026-04-12T16:26:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1f/b072a92369039ebef11b0a654be5134fcf3ed04c0f437faf9435ac9ba845/lxml-6.0.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:07dce892881179e11053066faca2da17b0eeb0bb7298f11bcf842a86db207dbd", size = 5227259, upload-time = "2026-04-12T16:26:47.083Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/dc97034f9d4c0c4d30875147d81fd2c0c7f3d261b109db36ed746bf8ab1d/lxml-6.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e4f97aee337b947e6699e5574c90d087d3e2ce517016241c07e7e98a28dca885", size = 5246190, upload-time = "2026-04-12T16:26:49.468Z" }, - { url = "https://files.pythonhosted.org/packages/f2/ef/85cb69835113583c2516fee07d0ffb4d824b557424b06ba5872c20ba6078/lxml-6.0.4-cp314-cp314t-win32.whl", hash = "sha256:064477c0d4c695aa1ea4b9c1c4ee9043ab740d12135b74c458cc658350adcd86", size = 3896005, upload-time = "2026-04-12T16:26:52.163Z" }, - { url = "https://files.pythonhosted.org/packages/3d/5e/2231f34cc54b8422b793593138d86d3fa4588fb2297d4ea0472390f25627/lxml-6.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:25bad2d8438f4ef5a7ad4a8d8bcaadde20c0daced8bdb56d46236b0a7d1cbdd0", size = 4391037, upload-time = "2026-04-12T16:26:54.398Z" }, - { url = "https://files.pythonhosted.org/packages/39/53/8ba3cd5984f8363635450c93f63e541a0721b362bb32ae0d8237d9674aee/lxml-6.0.4-cp314-cp314t-win_arm64.whl", hash = "sha256:1dcd9e6cb9b7df808ea33daebd1801f37a8f50e8c075013ed2a2343246727838", size = 3816184, upload-time = "2026-04-12T16:26:57.011Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/cee4cf203ef0bab5c52afc118da61d6b460c928f2893d40023cfa27e0b80/lxml-6.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8", size = 8576713, upload-time = "2026-04-18T04:32:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a7/eda05babeb7e046839204eaf254cd4d7c9130ce2bbf0d9e90ea41af5654d/lxml-6.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9", size = 4623874, upload-time = "2026-04-18T04:32:10.755Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e9/db5846de9b436b91890a62f29d80cd849ea17948a49bf532d5278ee69a9e/lxml-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03", size = 4949535, upload-time = "2026-04-18T04:34:06.657Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ba/0d3593373dcae1d68f40dc3c41a5a92f2544e68115eb2f62319a4c2a6500/lxml-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb", size = 5086881, upload-time = "2026-04-18T04:34:09.556Z" }, + { url = "https://files.pythonhosted.org/packages/43/76/759a7484539ad1af0d125a9afe9c3fb5f82a8779fd1f5f56319d9e4ea2fd/lxml-6.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c", size = 5031305, upload-time = "2026-04-18T04:34:12.336Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b9/c1f0daf981a11e47636126901fd4ab82429e18c57aeb0fc3ad2940b42d8b/lxml-6.1.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28", size = 5647522, upload-time = "2026-04-18T04:34:14.89Z" }, + { url = "https://files.pythonhosted.org/packages/31/e6/1f533dcd205275363d9ba3511bcec52fa2df86abf8abe6a5f2c599f0dc31/lxml-6.1.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086", size = 5239310, upload-time = "2026-04-18T04:34:17.652Z" }, + { url = "https://files.pythonhosted.org/packages/c3/8c/4175fb709c78a6e315ed814ed33be3defd8b8721067e70419a6cf6f971da/lxml-6.1.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f", size = 5350799, upload-time = "2026-04-18T04:34:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/6ffdebc5994975f0dde4acb59761902bd9d9bb84422b9a0bd239a7da9ca8/lxml-6.1.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292", size = 4697693, upload-time = "2026-04-18T04:34:23.541Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f1/565f36bd5c73294602d48e04d23f81ff4c8736be6ba5e1d1ec670ac9be80/lxml-6.1.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb", size = 5250708, upload-time = "2026-04-18T04:34:26.001Z" }, + { url = "https://files.pythonhosted.org/packages/5a/11/a68ab9dd18c5c499404deb4005f4bc4e0e88e5b72cd755ad96efec81d18d/lxml-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad", size = 5084737, upload-time = "2026-04-18T04:34:28.32Z" }, + { url = "https://files.pythonhosted.org/packages/ab/78/e8f41e2c74f4af564e6a0348aea69fb6daaefa64bc071ef469823d22cc18/lxml-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb", size = 4737817, upload-time = "2026-04-18T04:34:30.784Z" }, + { url = "https://files.pythonhosted.org/packages/06/2d/aa4e117aa2ce2f3b35d9ff246be74a2f8e853baba5d2a92c64744474603a/lxml-6.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f", size = 5670753, upload-time = "2026-04-18T04:34:33.675Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/dd745d50c0409031dbfcc4881740542a01e54d6f0110bd420fa7782110b8/lxml-6.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43", size = 5238071, upload-time = "2026-04-18T04:34:36.12Z" }, + { url = "https://files.pythonhosted.org/packages/3e/74/ad424f36d0340a904665867dab310a3f1f4c96ff4039698de83b77f44c1f/lxml-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585", size = 5264319, upload-time = "2026-04-18T04:34:39.035Z" }, + { url = "https://files.pythonhosted.org/packages/53/36/a15d8b3514ec889bfd6aa3609107fcb6c9189f8dc347f1c0b81eded8d87c/lxml-6.1.0-cp314-cp314-win32.whl", hash = "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f", size = 3657139, upload-time = "2026-04-18T04:32:20.006Z" }, + { url = "https://files.pythonhosted.org/packages/1a/a4/263ebb0710851a3c6c937180a9a86df1206fdfe53cc43005aa2237fd7736/lxml-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120", size = 4064195, upload-time = "2026-04-18T04:32:23.876Z" }, + { url = "https://files.pythonhosted.org/packages/80/68/2000f29d323b6c286de077ad20b429fc52272e44eae6d295467043e56012/lxml-6.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946", size = 3741870, upload-time = "2026-04-18T04:32:27.922Z" }, + { url = "https://files.pythonhosted.org/packages/30/e9/21383c7c8d43799f0da90224c0d7c921870d476ec9b3e01e1b2c0b8237c5/lxml-6.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c", size = 8827548, upload-time = "2026-04-18T04:32:15.094Z" }, + { url = "https://files.pythonhosted.org/packages/a5/01/c6bc11cd587030dd4f719f65c5657960649fe3e19196c844c75bf32cd0d6/lxml-6.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d", size = 4735866, upload-time = "2026-04-18T04:32:18.924Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/757132fff5f4acf25463b5298f1a46099f3a94480b806547b29ce5e385de/lxml-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9", size = 4969476, upload-time = "2026-04-18T04:34:41.889Z" }, + { url = "https://files.pythonhosted.org/packages/fd/fb/1bc8b9d27ed64be7c8903db6c89e74dc8c2cd9ec630a7462e4654316dc5b/lxml-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9", size = 5103719, upload-time = "2026-04-18T04:34:44.797Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e7/5bf82fa28133536a54601aae633b14988e89ed61d4c1eb6b899b023233aa/lxml-6.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7", size = 5027890, upload-time = "2026-04-18T04:34:47.634Z" }, + { url = "https://files.pythonhosted.org/packages/2d/20/e048db5d4b4ea0366648aa595f26bb764b2670903fc585b87436d0a5032c/lxml-6.1.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86", size = 5596008, upload-time = "2026-04-18T04:34:51.503Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c2/d10807bc8da4824b39e5bd01b5d05c077b6fd01bd91584167edf6b269d22/lxml-6.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb", size = 5224451, upload-time = "2026-04-18T04:34:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/3c/15/2ebea45bea427e7f0057e9ce7b2d62c5aba20c6b001cca89ed0aadb3ad41/lxml-6.1.0-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c", size = 5312135, upload-time = "2026-04-18T04:34:56.818Z" }, + { url = "https://files.pythonhosted.org/packages/31/e2/87eeae151b0be2a308d49a7ec444ff3eb192b14251e62addb29d0bf3778f/lxml-6.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f", size = 4639126, upload-time = "2026-04-18T04:34:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/a3/51/8a3f6a20902ad604dd746ec7b4000311b240d389dac5e9d95adefd349e0c/lxml-6.1.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773", size = 5232579, upload-time = "2026-04-18T04:35:02.658Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d2/650d619bdbe048d2c3f2c31edb00e35670a5e2d65b4fe3b61bce37b19121/lxml-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b", size = 5084206, upload-time = "2026-04-18T04:35:05.175Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8a/672ca1a3cbeabd1f511ca275a916c0514b747f4b85bdaae103b8fa92f307/lxml-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405", size = 4758906, upload-time = "2026-04-18T04:35:08.098Z" }, + { url = "https://files.pythonhosted.org/packages/be/f1/ef4b691da85c916cb2feb1eec7414f678162798ac85e042fa164419ac05c/lxml-6.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690", size = 5620553, upload-time = "2026-04-18T04:35:11.23Z" }, + { url = "https://files.pythonhosted.org/packages/59/17/94e81def74107809755ac2782fdad4404420f1c92ca83433d117a6d5acf0/lxml-6.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd", size = 5229458, upload-time = "2026-04-18T04:35:14.254Z" }, + { url = "https://files.pythonhosted.org/packages/21/55/c4be91b0f830a871fc1b0d730943d56013b683d4671d5198260e2eae722b/lxml-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180", size = 5247861, upload-time = "2026-04-18T04:35:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ca/77123e4d77df3cb1e968ade7b1f808f5d3a5c1c96b18a33895397de292c1/lxml-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2", size = 3897377, upload-time = "2026-04-18T04:32:07.656Z" }, + { url = "https://files.pythonhosted.org/packages/64/ce/3554833989d074267c063209bae8b09815e5656456a2d332b947806b05ff/lxml-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5", size = 4392701, upload-time = "2026-04-18T04:32:12.113Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a0/9b916c68c0e57752c07f8f64b30138d9d4059dbeb27b90274dedbea128ff/lxml-6.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac", size = 3817120, upload-time = "2026-04-18T04:32:15.803Z" }, ] [[package]] @@ -2563,11 +2563,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] @@ -2796,7 +2796,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.5" +version = "2.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2804,9 +2804,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/77/ca/b45c378e6e8d0b90577288b533e04e95b7afd61bb1d51b6c263176435489/pydantic-2.13.2-py3-none-any.whl", hash = "sha256:a525087f4c03d7e7456a3de89b64cd693d2229933bb1068b9af6befd5563694e", size = 471947, upload-time = "2026-04-17T09:31:57.541Z" }, ] [package.optional-dependencies] @@ -2816,41 +2816,43 @@ email = [ [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.46.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/d0/96/a50ccb6b539ae780f73cea74905468777680e30c6c3bdf714b9d4c116ea0/pydantic_core-2.46.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4f59b45f3ef8650c0c736a57f59031d47ed9df4c0a64e83796849d7d14863a2d", size = 2097111, upload-time = "2026-04-17T09:10:49.617Z" }, + { url = "https://files.pythonhosted.org/packages/34/5f/fdead7b3afa822ab6e5a18ee0ecffd54937de1877c01ed13a342e0fb3f07/pydantic_core-2.46.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a075a29ebef752784a91532a1a85be6b234ccffec0a9d7978a92696387c3da6", size = 1951904, upload-time = "2026-04-17T09:12:32.062Z" }, + { url = "https://files.pythonhosted.org/packages/95/e0/1c5d547e550cdab1bec737492aa08865337af6fe7fc9b96f7f45f17d9519/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d12d786e30c04a9d307c5d7080bf720d9bac7f1668191d8e37633a9562749e2", size = 1978667, upload-time = "2026-04-17T09:11:35.589Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/665ce629e218c8228302cb94beff4f6531082a2c87d3ecc3d5e63a26f392/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d5e6d6343b0b5dcacb3503b5de90022968da8ed0ab9ab39d3eda71c20cbf84e", size = 2046721, upload-time = "2026-04-17T09:11:47.725Z" }, + { url = "https://files.pythonhosted.org/packages/77/e9/6cb2cf60f54c1472bbdfce19d957553b43dbba79d1d7b2930a195c594785/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:233eebac0999b6b9ba76eb56f3ec8fce13164aa16b6d2225a36a79e0f95b5973", size = 2228483, upload-time = "2026-04-17T09:12:08.837Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/93e018dd5571f781ebaeda8c0cf65398489d5bee9b1f484df0b6149b43b9/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cc0eee720dd2f14f3b7c349469402b99ad81a174ab49d3533974529e9d93992", size = 2294663, upload-time = "2026-04-17T09:12:52.053Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/49e57ca55c770c93d9bb046666a54949b42e3c9099a0c5fe94557873fe30/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ee76bf2c9910513dbc19e7d82367131fa7508dedd6186a462393071cc11059", size = 2098742, upload-time = "2026-04-17T09:13:45.472Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b0/6e46b5cd3332af665f794b8cdeea206618a8630bd9e7bcc36864518fce81/pydantic_core-2.46.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:d61db38eb4ee5192f0c261b7f2d38e420b554df8912245e3546aee5c45e2fd78", size = 2125922, upload-time = "2026-04-17T09:12:54.304Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/40850c81585be443a2abfdf7f795f8fae831baf8e2f9b2133c8246ac671c/pydantic_core-2.46.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f09a713d17bcd55da8ab02ebd9110c5246a49c44182af213b5212800af8bc83", size = 2183000, upload-time = "2026-04-17T09:10:59.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/af/8493d7dfa03ebb7866909e577c6aa65ea0de7377b86023cc51d0c8e11db3/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:30cacc5fb696e64b8ef6fd31d9549d394dd7d52760db072eecb98e37e3af1677", size = 2180335, upload-time = "2026-04-17T09:12:57.01Z" }, + { url = "https://files.pythonhosted.org/packages/72/5b/1f6a344c4ffdf284da41c6067b82d5ebcbd11ce1b515ae4b662d4adb6f61/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:7ccfb105fcfe91a22bbb5563ad3dc124bc1aa75bfd2e53a780ab05f78cdf6108", size = 2330002, upload-time = "2026-04-17T09:12:02.958Z" }, + { url = "https://files.pythonhosted.org/packages/25/ff/9a694126c12d6d2f48a0cafa6f8eef88ef0d8825600e18d03ff2e896c3b2/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:13ffef637dc8370c249e5b26bd18e9a80a4fca3d809618c44e18ec834a7ca7a8", size = 2359920, upload-time = "2026-04-17T09:10:27.764Z" }, + { url = "https://files.pythonhosted.org/packages/51/c8/3a35c763d68a9cb2675eb10ef242cf66c5d4701b28ae12e688d67d2c180e/pydantic_core-2.46.2-cp314-cp314-win32.whl", hash = "sha256:1b0ab6d756ca2704a938e6c31b53f290c2f9c10d3914235410302a149de1a83e", size = 1953701, upload-time = "2026-04-17T09:13:30.021Z" }, + { url = "https://files.pythonhosted.org/packages/1a/6a/f2726a780365f7dfd89d62036f984f7acb99978c60c5e1fa7c0cb898ed11/pydantic_core-2.46.2-cp314-cp314-win_amd64.whl", hash = "sha256:99ebade8c9ada4df975372d8dd25883daa0e379a05f1cd0c99aa0c04368d01a6", size = 2071867, upload-time = "2026-04-17T09:10:39.205Z" }, + { url = "https://files.pythonhosted.org/packages/e1/79/76baacb9feba3d7c399b245ca1a29c74ea0db04ea693811374827eec2290/pydantic_core-2.46.2-cp314-cp314-win_arm64.whl", hash = "sha256:de87422197cf7f83db91d89c86a21660d749b3cd76cd8a45d115b8e675670f02", size = 2017252, upload-time = "2026-04-17T09:10:26.175Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3b/77c26938f817668d9ad9bab1a905cb23f11d9a3d4bf724d429b3e55a8eaf/pydantic_core-2.46.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:236f22b4a206b5b61db955396b7cf9e2e1ff77f372efe9570128ccfcd6a525eb", size = 2094545, upload-time = "2026-04-17T09:12:19.339Z" }, + { url = "https://files.pythonhosted.org/packages/fe/de/42c13f590e3c260966aa49bcdb1674774f975467c49abd51191e502bea28/pydantic_core-2.46.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c2012f64d2cd7cca50f49f22445aa5a88691ac2b4498ee0a9a977f8ca4f7289f", size = 1933953, upload-time = "2026-04-17T09:09:55.889Z" }, + { url = "https://files.pythonhosted.org/packages/4e/84/ebe3ebb3e2d8db656937cfa6f97f544cb7132f2307a4a7dfdcd0ea102a12/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d07d6c63106d3a9c9a333e2636f9c82c703b1a9e3b079299e58747964e4fdb72", size = 1974435, upload-time = "2026-04-17T09:10:12.371Z" }, + { url = "https://files.pythonhosted.org/packages/b9/15/0bf51ca6709477cd4ef86148b6d7844f3308f029eac361dd0383f1e17b1a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c326a2b4b85e959d9a1fc3a11f32f84611b6ec07c053e1828a860edf8d068208", size = 2031113, upload-time = "2026-04-17T09:10:00.752Z" }, + { url = "https://files.pythonhosted.org/packages/02/ae/b7b5af9b79db036d9e61a44c481c17a213dc8fc4b8b71fe6875a72fc778b/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac8a65e798f2462552c00d2e013d532c94d646729dda98458beaf51f9ec7b120", size = 2236325, upload-time = "2026-04-17T09:10:33.227Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ae/ecef7477b5a03d4a499708f7e75d2836452ebb70b776c2d64612b334f57a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a3c2bc1cc8164bedbc160b7bb1e8cc1e8b9c27f69ae4f9ae2b976cdae02b2dd", size = 2278135, upload-time = "2026-04-17T09:10:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/2f9d82faa47af6c39fc3f120145fd915971e1e0cb6b55b494fad9fdf8275/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69aa5e10b7e8b1bb4a6888650fd12fcbf11d396ca11d4a44de1450875702830", size = 2109071, upload-time = "2026-04-17T09:11:06.149Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9c/677cf10873fbd0b116575ab7b97c90482b21564f8a8040beb18edef7a577/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4e6df5c3301e65fb42bc5338bf9a1027a02b0a31dc7f54c33775229af474daf0", size = 2106028, upload-time = "2026-04-17T09:10:51.525Z" }, + { url = "https://files.pythonhosted.org/packages/d6/53/6a06183544daba51c059123a2064a99039df25f115a06bdb26f2ea177038/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c2f6e32548ac8d559b47944effcf8ae4d81c161f6b6c885edc53bc08b8f192d", size = 2164816, upload-time = "2026-04-17T09:11:56.187Z" }, + { url = "https://files.pythonhosted.org/packages/57/6f/10fcdd9e3eca66fc828eef0f6f5850f2dd3bca2c59e6e041fb8bc3da39be/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:b089a81c58e6ea0485562bbbbbca4f65c0549521606d5ef27fba217aac9b665a", size = 2166130, upload-time = "2026-04-17T09:10:03.804Z" }, + { url = "https://files.pythonhosted.org/packages/29/83/92d3fd0e0156cad2e3cb5c26de73794af78ac9fa0c22ab666e566dd67061/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:7f700a6d6f64112ae9193709b84303bbab84424ad4b47d0253301aabce9dfc70", size = 2316605, upload-time = "2026-04-17T09:12:45.249Z" }, + { url = "https://files.pythonhosted.org/packages/97/f1/facffdb970981068219582e499b8d0871ed163ffcc6b347de5c412669e4c/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:67db6814beaa5fefe91101ec7eb9efda613795767be96f7cf58b1ca8c9ca9972", size = 2358385, upload-time = "2026-04-17T09:09:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a1/b8160b2f22b2199467bc68581a4ed380643c16b348a27d6165c6c242d694/pydantic_core-2.46.2-cp314-cp314t-win32.whl", hash = "sha256:32fbc7447be8e3be99bf7869f7066308f16be55b61f9882c2cefc7931f5c7664", size = 1942373, upload-time = "2026-04-17T09:12:59.594Z" }, + { url = "https://files.pythonhosted.org/packages/0d/90/db89acabe5b150e11d1b59fe3d947dda2ef6abbfef5c82f056ff63802f5d/pydantic_core-2.46.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b317a2b97019c0b95ce99f4f901ae383f40132da6706cdf1731066a73394c25c", size = 2052078, upload-time = "2026-04-17T09:10:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/97/32/e19b83ceb07a3f1bb21798407790bbc9a31740158fd132b94139cb84e16c/pydantic_core-2.46.2-cp314-cp314t-win_arm64.whl", hash = "sha256:7dcb9d40930dfad7ab6b20bcc6ca9d2b030b0f347a0cd9909b54bd53ead521b1", size = 2016941, upload-time = "2026-04-17T09:12:34.447Z" }, ] [[package]] @@ -3055,11 +3057,11 @@ wheels = [ [[package]] name = "python-dotenv" -version = "1.2.1" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] @@ -3259,27 +3261,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.10" +version = "0.15.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/aa3f7d59a10ef6b14fe3431706f854dbf03c5976be614a9796d36326810c/ruff-0.15.10.tar.gz", hash = "sha256:d1f86e67ebfdef88e00faefa1552b5e510e1d35f3be7d423dc7e84e63788c94e", size = 4631728, upload-time = "2026-04-09T14:06:09.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/192f3d7103816158dfd5ea50d098ef2aec19194e6cbccd4b3485bdb2eb2d/ruff-0.15.11.tar.gz", hash = "sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33", size = 4637264, upload-time = "2026-04-16T18:46:26.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/00/a1c2fdc9939b2c03691edbda290afcd297f1f389196172826b03d6b6a595/ruff-0.15.10-py3-none-linux_armv6l.whl", hash = "sha256:0744e31482f8f7d0d10a11fcbf897af272fefdfcb10f5af907b18c2813ff4d5f", size = 10563362, upload-time = "2026-04-09T14:06:21.189Z" }, - { url = "https://files.pythonhosted.org/packages/5c/15/006990029aea0bebe9d33c73c3e28c80c391ebdba408d1b08496f00d422d/ruff-0.15.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b1e7c16ea0ff5a53b7c2df52d947e685973049be1cdfe2b59a9c43601897b22e", size = 10951122, upload-time = "2026-04-09T14:06:02.236Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c0/4ac978fe874d0618c7da647862afe697b281c2806f13ce904ad652fa87e4/ruff-0.15.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93cc06a19e5155b4441dd72808fdf84290d84ad8a39ca3b0f994363ade4cebb1", size = 10314005, upload-time = "2026-04-09T14:06:00.026Z" }, - { url = "https://files.pythonhosted.org/packages/da/73/c209138a5c98c0d321266372fc4e33ad43d506d7e5dd817dd89b60a8548f/ruff-0.15.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83e1dd04312997c99ea6965df66a14fb4f03ba978564574ffc68b0d61fd3989e", size = 10643450, upload-time = "2026-04-09T14:05:42.137Z" }, - { url = "https://files.pythonhosted.org/packages/ec/76/0deec355d8ec10709653635b1f90856735302cb8e149acfdf6f82a5feb70/ruff-0.15.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8154d43684e4333360fedd11aaa40b1b08a4e37d8ffa9d95fee6fa5b37b6fab1", size = 10379597, upload-time = "2026-04-09T14:05:49.984Z" }, - { url = "https://files.pythonhosted.org/packages/dc/be/86bba8fc8798c081e28a4b3bb6d143ccad3fd5f6f024f02002b8f08a9fa3/ruff-0.15.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab88715f3a6deb6bde6c227f3a123410bec7b855c3ae331b4c006189e895cef", size = 11146645, upload-time = "2026-04-09T14:06:12.246Z" }, - { url = "https://files.pythonhosted.org/packages/a8/89/140025e65911b281c57be1d385ba1d932c2366ca88ae6663685aed8d4881/ruff-0.15.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a768ff5969b4f44c349d48edf4ab4f91eddb27fd9d77799598e130fb628aa158", size = 12030289, upload-time = "2026-04-09T14:06:04.776Z" }, - { url = "https://files.pythonhosted.org/packages/88/de/ddacca9545a5e01332567db01d44bd8cf725f2db3b3d61a80550b48308ea/ruff-0.15.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ee3ef42dab7078bda5ff6a1bcba8539e9857deb447132ad5566a038674540d0", size = 11496266, upload-time = "2026-04-09T14:05:55.485Z" }, - { url = "https://files.pythonhosted.org/packages/bc/bb/7ddb00a83760ff4a83c4e2fc231fd63937cc7317c10c82f583302e0f6586/ruff-0.15.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51cb8cc943e891ba99989dd92d61e29b1d231e14811db9be6440ecf25d5c1609", size = 11256418, upload-time = "2026-04-09T14:05:57.69Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8d/55de0d35aacf6cd50b6ee91ee0f291672080021896543776f4170fc5c454/ruff-0.15.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e59c9bdc056a320fb9ea1700a8d591718b8faf78af065484e801258d3a76bc3f", size = 11288416, upload-time = "2026-04-09T14:05:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/68/cf/9438b1a27426ec46a80e0a718093c7f958ef72f43eb3111862949ead3cc1/ruff-0.15.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:136c00ca2f47b0018b073f28cb5c1506642a830ea941a60354b0e8bc8076b151", size = 10621053, upload-time = "2026-04-09T14:05:52.782Z" }, - { url = "https://files.pythonhosted.org/packages/4c/50/e29be6e2c135e9cd4cb15fbade49d6a2717e009dff3766dd080fcb82e251/ruff-0.15.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b80a2f3c9c8a950d6237f2ca12b206bccff626139be9fa005f14feb881a1ae8", size = 10378302, upload-time = "2026-04-09T14:06:14.361Z" }, - { url = "https://files.pythonhosted.org/packages/18/2f/e0b36a6f99c51bb89f3a30239bc7bf97e87a37ae80aa2d6542d6e5150364/ruff-0.15.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e3e53c588164dc025b671c9df2462429d60357ea91af7e92e9d56c565a9f1b07", size = 10850074, upload-time = "2026-04-09T14:06:16.581Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/874da392558ce087a0f9b709dc6ec0d60cbc694c1c772dab8d5f31efe8cb/ruff-0.15.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b0c52744cf9f143a393e284125d2576140b68264a93c6716464e129a3e9adb48", size = 11358051, upload-time = "2026-04-09T14:06:18.948Z" }, - { url = "https://files.pythonhosted.org/packages/e4/46/602938f030adfa043e67112b73821024dc79f3ab4df5474c25fa4c1d2d14/ruff-0.15.10-py3-none-win32.whl", hash = "sha256:d4272e87e801e9a27a2e8df7b21011c909d9ddd82f4f3281d269b6ba19789ca5", size = 10588964, upload-time = "2026-04-09T14:06:07.14Z" }, - { url = "https://files.pythonhosted.org/packages/25/b6/261225b875d7a13b33a6d02508c39c28450b2041bb01d0f7f1a83d569512/ruff-0.15.10-py3-none-win_amd64.whl", hash = "sha256:28cb32d53203242d403d819fd6983152489b12e4a3ae44993543d6fe62ab42ed", size = 11745044, upload-time = "2026-04-09T14:05:39.473Z" }, - { url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" }, + { url = "https://files.pythonhosted.org/packages/02/1e/6aca3427f751295ab011828e15e9bf452200ac74484f1db4be0197b8170b/ruff-0.15.11-py3-none-linux_armv6l.whl", hash = "sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7", size = 10607943, upload-time = "2026-04-16T18:46:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/e7/26/1341c262e74f36d4e84f3d6f4df0ac68cd53331a66bfc5080daa17c84c0b/ruff-0.15.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e", size = 10988592, upload-time = "2026-04-16T18:46:00.742Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/850b1d6ffa9564fbb6740429bad53df1094082fe515c8c1e74b6d8d05f18/ruff-0.15.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb", size = 10338501, upload-time = "2026-04-16T18:46:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/f2/11/cc1284d3e298c45a817a6aadb6c3e1d70b45c9b36d8d9cce3387b495a03a/ruff-0.15.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4", size = 10670693, upload-time = "2026-04-16T18:46:41.941Z" }, + { url = "https://files.pythonhosted.org/packages/ce/9e/f8288b034ab72b371513c13f9a41d9ba3effac54e24bfb467b007daee2ca/ruff-0.15.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb", size = 10416177, upload-time = "2026-04-16T18:46:21.717Z" }, + { url = "https://files.pythonhosted.org/packages/85/71/504d79abfd3d92532ba6bbe3d1c19fada03e494332a59e37c7c2dabae427/ruff-0.15.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d", size = 11221886, upload-time = "2026-04-16T18:46:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/43/5a/947e6ab7a5ad603d65b474be15a4cbc6d29832db5d762cd142e4e3a74164/ruff-0.15.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7", size = 12075183, upload-time = "2026-04-16T18:46:07.944Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a1/0b7bb6268775fdd3a0818aee8efd8f5b4e231d24dd4d528ced2534023182/ruff-0.15.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e", size = 11516575, upload-time = "2026-04-16T18:46:31.687Z" }, + { url = "https://files.pythonhosted.org/packages/30/c3/bb5168fc4d233cc06e95f482770d0f3c87945a0cd9f614b90ea8dc2f2833/ruff-0.15.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431", size = 11306537, upload-time = "2026-04-16T18:46:36.988Z" }, + { url = "https://files.pythonhosted.org/packages/e4/92/4cfae6441f3967317946f3b788136eecf093729b94d6561f963ed810c82e/ruff-0.15.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19", size = 11296813, upload-time = "2026-04-16T18:46:24.182Z" }, + { url = "https://files.pythonhosted.org/packages/43/26/972784c5dde8313acde8ac71ba8ac65475b85db4a2352a76c9934361f9bc/ruff-0.15.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890", size = 10633136, upload-time = "2026-04-16T18:46:39.802Z" }, + { url = "https://files.pythonhosted.org/packages/5b/53/3985a4f185020c2f367f2e08a103032e12564829742a1b417980ce1514a0/ruff-0.15.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5", size = 10424701, upload-time = "2026-04-16T18:46:10.381Z" }, + { url = "https://files.pythonhosted.org/packages/d3/57/bf0dfb32241b56c83bb663a826133da4bf17f682ba8c096973065f6e6a68/ruff-0.15.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0", size = 10873887, upload-time = "2026-04-16T18:46:29.157Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/e48076b2a57dc33ee8c7a957296f97c744ca891a8ffb4ffb1aaa3b3f517d/ruff-0.15.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c", size = 11404316, upload-time = "2026-04-16T18:46:19.462Z" }, + { url = "https://files.pythonhosted.org/packages/88/27/0195d15fe7a897cbcba0904792c4b7c9fdd958456c3a17d2ea6093716a9a/ruff-0.15.11-py3-none-win32.whl", hash = "sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3", size = 10655535, upload-time = "2026-04-16T18:46:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5e/c927b325bd4c1d3620211a4b96f47864633199feed60fa936025ab27e090/ruff-0.15.11-py3-none-win_amd64.whl", hash = "sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3", size = 11779692, upload-time = "2026-04-16T18:46:17.268Z" }, + { url = "https://files.pythonhosted.org/packages/63/b6/aeadee5443e49baa2facd51131159fd6301cc4ccfc1541e4df7b021c37dd/ruff-0.15.11-py3-none-win_arm64.whl", hash = "sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4", size = 11032614, upload-time = "2026-04-16T18:46:34.487Z" }, ] [[package]] @@ -3308,7 +3310,7 @@ wheels = [ [[package]] name = "selenium" -version = "4.41.0" +version = "4.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -3318,22 +3320,22 @@ dependencies = [ { name = "urllib3", extra = ["socks"] }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/7c/133d00d6d013a17d3f39199f27f1a780ec2e95d7b9aa997dc1b8ac2e62a7/selenium-4.41.0.tar.gz", hash = "sha256:003e971f805231ad63e671783a2b91a299355d10cefb9de964c36ff3819115aa", size = 937872, upload-time = "2026-02-20T03:42:06.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/6a/fe950b498a3c570ab538ad1c2b60f18863eecf077a865eea4459f3fa78a9/selenium-4.43.0.tar.gz", hash = "sha256:bada5c08a989f812728a4b5bea884d8e91894e939a441cc3a025201ce718581e", size = 967747, upload-time = "2026-04-10T06:47:03.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/d6/e4160989ef6b272779af6f3e5c43c3ba9be6687bdc21c68c3fb220e555b3/selenium-4.41.0-py3-none-any.whl", hash = "sha256:b8ccde8d2e7642221ca64af184a92c19eee6accf2e27f20f30472f5efae18eb1", size = 9532858, upload-time = "2026-02-20T03:42:03.218Z" }, + { url = "https://files.pythonhosted.org/packages/82/c7/0c55fbb0275fc368676ea50514ce7d7839d799a8b3ff8425f380186c7626/selenium-4.43.0-py3-none-any.whl", hash = "sha256:4f97639055dcfa9eadf8ccf549ba7b0e49c655d4e2bde19b9a44e916b754e769", size = 9573091, upload-time = "2026-04-10T06:47:01.134Z" }, ] [[package]] name = "sentry-sdk" -version = "2.57.0" +version = "2.58.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/87/46c0406d8b5ddd026f73adaf5ab75ce144219c41a4830b52df4b9ab55f7f/sentry_sdk-2.57.0.tar.gz", hash = "sha256:4be8d1e71c32fb27f79c577a337ac8912137bba4bcbc64a4ec1da4d6d8dc5199", size = 435288, upload-time = "2026-03-31T09:39:29.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/b3/fb8291170d0e844173164709fc0fa0c221ed75a5da740c8746f2a83b4eb1/sentry_sdk-2.58.0.tar.gz", hash = "sha256:c1144d947352d54e5b7daa63596d9f848adf684989c06c4f5a659f0c85a18f6f", size = 438764, upload-time = "2026-04-13T17:23:26.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/64/982e07b93219cb52e1cca5d272cb579e2f3eb001956c9e7a9a6d106c9473/sentry_sdk-2.57.0-py2.py3-none-any.whl", hash = "sha256:812c8bf5ff3d2f0e89c82f5ce80ab3a6423e102729c4706af7413fd1eb480585", size = 456489, upload-time = "2026-03-31T09:39:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/fa/eb/d875669993b762556ae8b2efd86219943b4c0864d22204d622a9aee3052b/sentry_sdk-2.58.0-py2.py3-none-any.whl", hash = "sha256:688d1c704ddecf382ea3326f21a67453d4caa95592d722b7c780a36a9d23109e", size = 460919, upload-time = "2026-04-13T17:23:24.675Z" }, ] [[package]] @@ -3517,7 +3519,7 @@ wheels = [ [[package]] name = "twilio" -version = "9.10.4" +version = "9.10.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -3525,9 +3527,9 @@ dependencies = [ { name = "pyjwt" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/28/9ffb8f01fc014bf634f0731bd562fed6209bc54d4249c69acfe68cf70302/twilio-9.10.4.tar.gz", hash = "sha256:2125e0027288903e7e8954ae6d5b1b593a205c28b3d72fa3fdd0b5f25825de04", size = 1637481, upload-time = "2026-03-24T13:04:11.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/97/c439bc2c058f8a24edd732f5cc82adedd8794bcc2da0836c2eff1e2dbe91/twilio-9.10.5.tar.gz", hash = "sha256:d9f93b9280349ee7b52e7f17a0600fd7bfd0f7ff88eb00c40270164bc058743f", size = 1641690, upload-time = "2026-04-14T09:52:09.392Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/3c/3296ee9f141b83623ff2c6c4d22093f7d2a8ea7455f98f705ee5f969bcd7/twilio-9.10.4-py2.py3-none-any.whl", hash = "sha256:5e8414a88c009f40b2ab2a1f00232f5f459f724c7b4bdf25a935c7d7b297a8a3", size = 2279546, upload-time = "2026-03-24T13:04:10.383Z" }, + { url = "https://files.pythonhosted.org/packages/4a/97/4fdde5e54fcbb789aa1a70c371f2f33d7eb1e58e8a6131fdbd8a98490976/twilio-9.10.5-py2.py3-none-any.whl", hash = "sha256:7972db54496fbf501b238f34d1f717f80ff22720313dc706632787aad5934997", size = 2284944, upload-time = "2026-04-14T09:52:07.333Z" }, ] [[package]] diff --git a/web/package-lock.json b/web/package-lock.json index 1a38b5e54a..1e71a28a90 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -21,7 +21,7 @@ "@codemirror/theme-one-dark": "^6.1.3", "@eslint/js": "^9.39.3", "@floating-ui/dom": "^1.7.6", - "@formatjs/intl-listformat": "^8.3.1", + "@formatjs/intl-listformat": "^8.3.2", "@fortawesome/fontawesome-free": "^7.2.0", "@goauthentik/api": "0.0.0", "@goauthentik/core": "^1.0.0", @@ -43,7 +43,7 @@ "@patternfly/elements": "^4.3.1", "@patternfly/patternfly": "^4.224.2", "@playwright/test": "^1.59.1", - "@sentry/browser": "^10.47.0", + "@sentry/browser": "^10.48.0", "@storybook/addon-docs": "^10.3.5", "@storybook/addon-links": "^10.3.5", "@storybook/web-components": "^10.3.5", @@ -51,7 +51,7 @@ "@types/codemirror": "^5.60.17", "@types/grecaptcha": "^3.0.9", "@types/guacamole-common-js": "^1.5.5", - "@types/node": "^25.5.2", + "@types/node": "^25.6.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^8.57.2", @@ -75,10 +75,10 @@ "eslint-plugin-lit": "^2.2.1", "eslint-plugin-wc": "^3.1.0", "fuse.js": "^7.3.0", - "globals": "^17.4.0", + "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "hastscript": "^9.0.1", - "knip": "^6.3.1", + "knip": "^6.4.1", "lex": "^2025.11.0", "lit": "^3.3.2", "lit-analyzer": "^2.0.3", @@ -91,7 +91,7 @@ "pino": "^10.3.1", "pino-pretty": "^13.1.2", "playwright": "^1.58.2", - "prettier": "^3.8.2", + "prettier": "^3.8.3", "prettier-plugin-packagejson": "^3.0.2", "pseudolocale": "^2.2.0", "rapidoc": "^9.3.8", @@ -111,7 +111,7 @@ "ts-pattern": "^5.9.0", "turnstile-types": "^1.2.3", "type-fest": "^5.5.0", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.57.2", "unist-util-visit": "^5.1.0", "vite": "^8.0.8", @@ -131,7 +131,7 @@ "@rollup/rollup-darwin-arm64": "^4.57.1", "@rollup/rollup-linux-arm64-gnu": "^4.57.1", "@rollup/rollup-linux-x64-gnu": "^4.57.1", - "chromedriver": "^147.0.2" + "chromedriver": "^147.0.4" } }, "node_modules/@adobe/css-tools": { @@ -1304,46 +1304,28 @@ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, - "node_modules/@formatjs/bigdecimal": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.0.tgz", - "integrity": "sha512-GeaxHZbUoYvHL9tC5eltHLs+1zU70aPw0s7LwqgktIzF5oMhNY4o4deEtusJMsq7WFJF3Ye2zQEzdG8beVk73w==", - "license": "MIT" - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.2.0.tgz", - "integrity": "sha512-dHnqHgBo6GXYGRsepaE1wmsC2etaivOWd5VaJstZd+HI2zR3DCUjbDVZRtoPGkkXZmyHvBwrdEUuqfvzhF/DtQ==", - "license": "MIT", - "dependencies": { - "@formatjs/bigdecimal": "0.2.0", - "@formatjs/fast-memoize": "3.1.1", - "@formatjs/intl-localematcher": "0.8.2" - } - }, "node_modules/@formatjs/fast-memoize": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.1.tgz", - "integrity": "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.2.tgz", + "integrity": "sha512-vPnriihkfK0lzoQGaXq+qXH23VsYyansRTkTgo2aTG0k1NjLFyZimFVdfj4C9JkSE5dm7CEngcQ5TTc1yAyBfQ==", "license": "MIT" }, "node_modules/@formatjs/intl-listformat": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-8.3.1.tgz", - "integrity": "sha512-82pPPyPOzkFMBbUmuseMwckFtpT+meZSxZSaxyD/3wRo5DZEq08hBUe1uhzNmukI8hwJFH5mIMBwSIAuse0RDQ==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-8.3.2.tgz", + "integrity": "sha512-PH8V1YVSm+AcrQ1D9Th5wxWo74vDs1V1rRGz9PCFm1HTBtZLVMN3P/+tDcpizQBvvdGNwUqHBOdxN5ZZtlq2bQ==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "3.2.0", - "@formatjs/intl-localematcher": "0.8.2" + "@formatjs/intl-localematcher": "0.8.3" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.2.tgz", - "integrity": "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.3.tgz", + "integrity": "sha512-pHUjWb9NuhnMs8+PxQdzBtZRFJHlGhrURGAbm6Ltwl82BFajeuiIR3jblSa7ia3r62rXe/0YtVpUG3xWr5bFCA==", "license": "MIT", "dependencies": { - "@formatjs/fast-memoize": "3.1.1" + "@formatjs/fast-memoize": "3.1.2" } }, "node_modules/@fortawesome/fontawesome-free": { @@ -3609,75 +3591,75 @@ "license": "MIT" }, "node_modules/@sentry-internal/browser-utils": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.47.0.tgz", - "integrity": "sha512-bVFRAeJWMBcBCvJKIFCMJ1/yQToL4vPGqfmlnDZeypcxkqUDKQ/Y3ziLHXoDL2sx0lagcgU2vH1QhCQ67Aujjw==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.48.0.tgz", + "integrity": "sha512-SCiTLBXzugFKxev6NoKYBIhQoDk0gUh0AVVVepCBqfCJiWBG01Zvv0R5tCVohr4cWRllkQ8mlBdNQd/I7s9tdA==", "license": "MIT", "dependencies": { - "@sentry/core": "10.47.0" + "@sentry/core": "10.48.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/feedback": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.47.0.tgz", - "integrity": "sha512-pdvMmi4dQpX5S/vAAzrhHPIw3T3HjUgDNgUiCBrlp7N9/6zGO2gNPhUnNekP+CjgI/z0rvf49RLqlDenpNrMOg==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.48.0.tgz", + "integrity": "sha512-tGkEyOM1HDS9qebDphUMEnyk3qq/50AnuTBiFmMJyjNzowylVGmRRk0sr3xkmbVHCDXQCiYnDmSVlJ2x4SDMrQ==", "license": "MIT", "dependencies": { - "@sentry/core": "10.47.0" + "@sentry/core": "10.48.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.47.0.tgz", - "integrity": "sha512-ScdovxP7hJxgMt70+7hFvwT02GIaIUAxdEM/YPsayZBeCoAukPW8WiwztJfoKtsfPyKJ5A6f0H3PIxTPcA9Row==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.48.0.tgz", + "integrity": "sha512-sevRTePfuk4PNuz9KAKpmTZEomAU0aLXyIhOwA0OnUDdxPhkY8kq5lwDbuxTHv6DQUjUX3YgFbY45VH1JEqHKA==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "10.47.0", - "@sentry/core": "10.47.0" + "@sentry-internal/browser-utils": "10.48.0", + "@sentry/core": "10.48.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.47.0.tgz", - "integrity": "sha512-A5OY8friSe6g8WAK4L8IeOPiEd9D3Ps40DzRH5j2f6SUja0t90mKMvHRcRf8zq0d4BkdB+JM7tjOkwxpuv8heA==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.48.0.tgz", + "integrity": "sha512-9nWuN2z4O+iwbTfuYV5ZmngBgJU/ZxfOo47A5RJP3Nu/kl59aJ1lUhILYOKyeNOIC/JyeERmpIcTxnlPXQzZ3Q==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "10.47.0", - "@sentry/core": "10.47.0" + "@sentry-internal/replay": "10.48.0", + "@sentry/core": "10.48.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry/browser": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.47.0.tgz", - "integrity": "sha512-rC0agZdxKA5XWfL4VwPOr/rJMogXDqZgnVzr93YWpFn9DMZT/7LzxSJVPIJwRUjx3bFEby3PcTa3YaX7pxm1AA==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.48.0.tgz", + "integrity": "sha512-4jt2zX2ExgFcNe2x+W+/k81fmDUsOrquGtt028CiGuDuma6kEsWBI4JbooT1jhj2T+eeUxe3YGbM23Zhh7Ghhw==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "10.47.0", - "@sentry-internal/feedback": "10.47.0", - "@sentry-internal/replay": "10.47.0", - "@sentry-internal/replay-canvas": "10.47.0", - "@sentry/core": "10.47.0" + "@sentry-internal/browser-utils": "10.48.0", + "@sentry-internal/feedback": "10.48.0", + "@sentry-internal/replay": "10.48.0", + "@sentry-internal/replay-canvas": "10.48.0", + "@sentry/core": "10.48.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry/core": { - "version": "10.47.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.47.0.tgz", - "integrity": "sha512-nsYRAx3EWezDut+Zl+UwwP07thh9uY7CfSAi2whTdcJl5hu1nSp2z8bba7Vq/MGbNLnazkd3A+GITBEML924JA==", + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.48.0.tgz", + "integrity": "sha512-h8F+fXVwYC9ro5ZaO8V+v3vqc0awlXHGblEAuVxSGgh4IV/oFX+QVzXeDTTrFOFS6v/Vn5vAyu240eJrJAS6/g==", "license": "MIT", "engines": { "node": ">=18" @@ -4572,9 +4554,9 @@ } }, "node_modules/@swc/core": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.24.tgz", - "integrity": "sha512-5Hj8aNasue7yusUt8LGCUe/AjM7RMAce8ZoyDyiFwx7Al+GbYKL+yE7g4sJk8vEr1dKIkTRARkNIJENc4CjkBQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.30.tgz", + "integrity": "sha512-R8VQbQY1BZcbIF2p3gjlTCwAQzx1A194ugWfwld5y+WgVVWqVKm7eURGGOVbQVubgKWzidP2agomBbg96rZilQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4589,18 +4571,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.24", - "@swc/core-darwin-x64": "1.15.24", - "@swc/core-linux-arm-gnueabihf": "1.15.24", - "@swc/core-linux-arm64-gnu": "1.15.24", - "@swc/core-linux-arm64-musl": "1.15.24", - "@swc/core-linux-ppc64-gnu": "1.15.24", - "@swc/core-linux-s390x-gnu": "1.15.24", - "@swc/core-linux-x64-gnu": "1.15.24", - "@swc/core-linux-x64-musl": "1.15.24", - "@swc/core-win32-arm64-msvc": "1.15.24", - "@swc/core-win32-ia32-msvc": "1.15.24", - "@swc/core-win32-x64-msvc": "1.15.24" + "@swc/core-darwin-arm64": "1.15.30", + "@swc/core-darwin-x64": "1.15.30", + "@swc/core-linux-arm-gnueabihf": "1.15.30", + "@swc/core-linux-arm64-gnu": "1.15.30", + "@swc/core-linux-arm64-musl": "1.15.30", + "@swc/core-linux-ppc64-gnu": "1.15.30", + "@swc/core-linux-s390x-gnu": "1.15.30", + "@swc/core-linux-x64-gnu": "1.15.30", + "@swc/core-linux-x64-musl": "1.15.30", + "@swc/core-win32-arm64-msvc": "1.15.30", + "@swc/core-win32-ia32-msvc": "1.15.30", + "@swc/core-win32-x64-msvc": "1.15.30" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -4612,9 +4594,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.24.tgz", - "integrity": "sha512-uM5ZGfFXjtvtJ+fe448PVBEbn/CSxS3UAyLj3O9xOqKIWy3S6hPTXSPbszxkSsGDYKi+YFhzAsR4r/eXLxEQ0g==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.30.tgz", + "integrity": "sha512-VvpP+vq08HmGYewMWvrdsxh9s2lthz/808zXm8Yu5kaqeR8Yia2b0eYXleHQ3VAjoStUDk6LzTheBW9KXYQdMA==", "cpu": [ "arm64" ], @@ -4628,9 +4610,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.24.tgz", - "integrity": "sha512-fMIb/Zfn929pw25VMBhV7Ji2Dl+lCWtUPNdYJQYOke+00E5fcQ9ynxtP8+qhUo/HZc+mYQb1gJxwHM9vty+lXg==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.30.tgz", + "integrity": "sha512-WiJA0hiZI3nwQAO6mu5RqigtWGDtth4Hiq6rbZxAaQyhIcqKIg5IoMRc1Y071lrNJn29eEDMC86Rq58xgUxlDg==", "cpu": [ "x64" ], @@ -4644,9 +4626,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.24.tgz", - "integrity": "sha512-vOkjsyjjxnoYx3hMEWcGxQrMgnNrRm6WAegBXrN8foHtDAR+zpdhpGF5a4lj1bNPgXAvmysjui8cM1ov/Clkaw==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.30.tgz", + "integrity": "sha512-YANuFUo48kIT6plJgCD0keae9HFXfjxsbvsgevqc0hr/07X/p7sAWTFOGYEc2SXcASaK7UvuQqzlbW8pr7R79g==", "cpu": [ "arm" ], @@ -4660,9 +4642,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.24.tgz", - "integrity": "sha512-h/oNu+upkXJ6Cicnq7YGVj9PkdfarLCdQa8l/FlHYvfv8CEiMaeeTnpLU7gSBH/rGxosM6Qkfa/J9mThGF9CLA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.30.tgz", + "integrity": "sha512-VndG8jaR4ugY6u+iVOT0Q+d2fZd7sLgjPgN8W/Le+3EbZKl+cRfFxV7Eoz4gfLqhmneZPdcIzf9T3LkgkmqNLg==", "cpu": [ "arm64" ], @@ -4676,9 +4658,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.24.tgz", - "integrity": "sha512-ZpF/pRe1guk6sKzQI9D1jAORtjTdNlyeXn9GDz8ophof/w2WhojRblvSDJaGe7rJjcPN8AaOkhwdRUh7q8oYIg==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.30.tgz", + "integrity": "sha512-1SYGs2l0Yyyi0pR/P/NKz/x0kqxkoiw+BXeJjLUdecSk/KasncWlJrc6hOvFSgKHOBrzgM5jwuluKtlT8dnrcA==", "cpu": [ "arm64" ], @@ -4692,9 +4674,9 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.24.tgz", - "integrity": "sha512-QZEsZfisHTSJlmyChgDFNmKPb3W6Lhbfo/O76HhIngfEdnQNmukS38/VSe1feho+xkV5A5hETyCbx3sALBZKAQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.30.tgz", + "integrity": "sha512-TXREtiXeRhbfDFbmhnkIsXpKfzbfT73YkV2ZF6w0sfxgjC5zI2ZAbaCOq25qxvegofj2K93DtOpm9RLaBgqR2g==", "cpu": [ "ppc64" ], @@ -4708,9 +4690,9 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.24.tgz", - "integrity": "sha512-DLdJKVsJgglqQrJBuoUYNmzm3leI7kUZhLbZGHv42onfKsGf6JDS3+bzCUQfte/XOqDjh/tmmn1DR/CF/tCJFw==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.30.tgz", + "integrity": "sha512-DCR2YYeyd6DQE4OuDhImouuNcjXEiEdnn1Y0DyGteugPEDvVuvYk8Xddi+4o2SgWH6jiW8/I+3emZvbep1NC+g==", "cpu": [ "s390x" ], @@ -4724,9 +4706,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.24.tgz", - "integrity": "sha512-IpLYfposPA/XLxYOKpRfeccl1p5dDa3+okZDHHTchBkXEaVCnq5MADPmIWwIYj1tudt7hORsEHccG5no6IUQRw==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.30.tgz", + "integrity": "sha512-5Pizw3NgfOJ5BJOBK8TIRa59xFW2avESTOBDPTAYwZYa1JNDs+KMF9lUfjJiJLM5HiMs/wPheA9eiT0q9m2AoA==", "cpu": [ "x64" ], @@ -4740,9 +4722,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.24.tgz", - "integrity": "sha512-JHy3fMSc0t/EPWgo74+OK5TGr51aElnzqfUPaiRf2qJ/BfX5CUCfMiWVBuhI7qmVMBnk1jTRnL/xZnOSHDPLYg==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.30.tgz", + "integrity": "sha512-qyqydP/wyH8alcIP4a2hnGSjHLJjm9H7yDFup+CPy9oTahFgLLwnNcv5UHXqO2Qs3AIND+cls5f/Bb6hqpxdgA==", "cpu": [ "x64" ], @@ -4756,9 +4738,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.24.tgz", - "integrity": "sha512-Txj+qUH1z2bUd1P3JvwByfjKFti3cptlAxhWgmunBUUxy/IW3CXLZ6l6Gk4liANadKkU71nIU1X30Z5vpMT3BA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.30.tgz", + "integrity": "sha512-CaQENgDHVGOg1mSF5sQVgvfFHG9kjMor2rkLMLeLOkfZYNj13ppnJ9+lfaBZLZUMMbnlGQnavCJb8PVBUOso7Q==", "cpu": [ "arm64" ], @@ -4772,9 +4754,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.24.tgz", - "integrity": "sha512-15D/nl3XwrhFpMv+MADFOiVwv3FvH9j8c6Rf8EXBT3Q5LoMh8YnDnSgPYqw1JzPnksvsBX6QPXLiPqmcR/Z4qQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.30.tgz", + "integrity": "sha512-30VdLeGk6fugiUs/kUdJ/pAg7z/zpvVbR11RH60jZ0Z42WIeIniYx0rLEWN7h/pKJ3CopqsQ3RsogCAkRKiA2g==", "cpu": [ "ia32" ], @@ -4788,9 +4770,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.24", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.24.tgz", - "integrity": "sha512-PR0PlTlPra2JbaDphrOAzm6s0v9rA0F17YzB+XbWD95B4g2cWcZY9LAeTa4xll70VLw9Jr7xBrlohqlQmelMFQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.30.tgz", + "integrity": "sha512-4iObHPR+Q4oDY110EF5SF5eIaaVJNpMdG9C0q3Q92BsJ5y467uHz7sYQhP60WYlLFsLQ1el2YrIPUItUAQGOKg==", "cpu": [ "x64" ], @@ -4906,13 +4888,6 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT", - "optional": true - }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -5299,12 +5274,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", - "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.19.0" } }, "node_modules/@types/ramda": { @@ -6176,9 +6151,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6206,13 +6181,13 @@ } }, "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", "license": "MIT", "optional": true, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/ajv": { @@ -6640,9 +6615,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.2.tgz", - "integrity": "sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.0.tgz", + "integrity": "sha512-5K9eNNn7ywHPsYnFwjKgYH8Hf8B5emh7JKcPaVjjrMJFQQwGpwowEnZNEtHs7DfR7hCZsmaK3VA4HUK0YarT+w==", "license": "MIT", "optional": true, "engines": { @@ -7258,18 +7233,18 @@ } }, "node_modules/chromedriver": { - "version": "147.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-147.0.2.tgz", - "integrity": "sha512-CgbtfPbdE3kse2gJdVVjvpRZtPV6pooaIVTJKdCj8LaDIMJvco57t2lXJcQObjfg2bhGN7RiGOk0iKpY2vLgLQ==", + "version": "147.0.4", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-147.0.4.tgz", + "integrity": "sha512-eRNbfkoTvAsSfFODM4QVhs3cXB/B4/nFHeI6+ycuKan5e3bJrq8njuLTBHHOLbL0dggxEpMHLiczJUQa+Gw3JA==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.13.5", + "axios": "^1.15.0", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", - "proxy-agent": "^6.5.0", + "proxy-agent": "^8.0.1", "proxy-from-env": "^2.0.0", "tcp-port-used": "^1.0.2" }, @@ -8014,13 +7989,13 @@ } }, "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-8.0.0.tgz", + "integrity": "sha512-6UHfyCux51b8PTGDgveqtz1tvphBku5DrMKKJbFAZAJOI2zsjDpDoYE1+QGj7FOMS4BdTFNJsJiR3zEB0xH0yQ==", "license": "MIT", "optional": true, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/data-view-buffer": { @@ -8273,9 +8248,9 @@ } }, "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-7.0.1.tgz", + "integrity": "sha512-ABErK0IefDSyHjlPH7WUEenIAX2rPPnrDcDM+TS3z3+zu9TfyKKi07BQM+8rmxpdE2y1v5fjjdoAS/x4D2U60w==", "license": "MIT", "optional": true, "dependencies": { @@ -8284,7 +8259,10 @@ "esprima": "^4.0.1" }, "engines": { - "node": ">= 14" + "node": ">= 20" + }, + "peerDependencies": { + "quickjs-wasi": "^2.2.0" } }, "node_modules/delaunator": { @@ -10032,18 +10010,18 @@ } }, "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-8.0.0.tgz", + "integrity": "sha512-CqtZlMKvfJeY0Zxv8wazDwXmSKmnMnsmNy8j8+wudi8EyG/pMUB1NqHc+Tv1QaNtpYsK9nOYjb7r7Ufu32RPSw==", "license": "MIT", "optional": true, "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", + "basic-ftp": "^5.2.0", + "data-uri-to-buffer": "8.0.0", "debug": "^4.3.4" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/git-hooks-list": { @@ -10068,9 +10046,9 @@ } }, "node_modules/globals": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", "license": "MIT", "engines": { "node": ">=18" @@ -10560,17 +10538,17 @@ "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.0.0.tgz", + "integrity": "sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.0", + "agent-base": "9.0.0", "debug": "^4.3.4" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/http2-wrapper": { @@ -10599,17 +10577,17 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.0.0.tgz", + "integrity": "sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "agent-base": "9.0.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/human-signals": { @@ -11583,9 +11561,9 @@ } }, "node_modules/knip": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.3.1.tgz", - "integrity": "sha512-22kLJloVcOVOAudCxlFOC0ICAMme7dKsS7pVTEnrmyKGpswb8ieznvAiSKUeFVDJhb01ect6dkDc1Ha1g1sPpg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.4.1.tgz", + "integrity": "sha512-Ry+ywmDFSZvKp/jx7LxMgsZWRTs931alV84e60lh0Stf6kSRYqSIUTkviyyDFRcSO3yY1Kpbi83OirN+4lA2Xw==", "funding": [ { "type": "github", @@ -13674,9 +13652,9 @@ } }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "license": "MIT", "optional": true, "engines": { @@ -14416,37 +14394,40 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-9.0.1.tgz", + "integrity": "sha512-3ZOSpLboOlpW4yp8Cuv21KlTULRqyJ5Uuad3wXpSKFrxdNgcHEyoa22GRaZ2UlgCVuR6z+5BiavtYVvbajL/Yw==", "license": "MIT", "optional": true, "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", + "agent-base": "9.0.0", "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" + "get-uri": "8.0.0", + "http-proxy-agent": "9.0.0", + "https-proxy-agent": "9.0.0", + "pac-resolver": "9.0.1", + "quickjs-wasi": "^2.2.0", + "socks-proxy-agent": "10.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-9.0.1.tgz", + "integrity": "sha512-lJbS008tmkj08VhoM8Hzuv/VE5tK9MS0OIQ/7+s0lIF+BYhiQWFYzkSpML7lXs9iBu2jfmzBTLzhe9n6BX+dYw==", "license": "MIT", "optional": true, "dependencies": { - "degenerator": "^5.0.0", + "degenerator": "7.0.1", "netmask": "^2.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 20" + }, + "peerDependencies": { + "quickjs-wasi": "^2.2.0" } }, "node_modules/package-manager-detector": { @@ -14866,9 +14847,9 @@ } }, "node_modules/prettier": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", - "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -15005,29 +14986,29 @@ } }, "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", + "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", "license": "MIT" }, "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-8.0.1.tgz", + "integrity": "sha512-kccqGBqHZXR8onQhY/ganJjoO8QIKKRiFBhPOzbTZK16attzSZ/0XSmp9H7jrRxPKHjhGyx1q32lMPrJ3uLFgA==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", + "agent-base": "9.0.0", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", + "http-proxy-agent": "9.0.0", + "https-proxy-agent": "9.0.0", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" + "pac-proxy-agent": "9.0.1", + "proxy-from-env": "^2.0.0", + "socks-proxy-agent": "10.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/proxy-agent/node_modules/lru-cache": { @@ -15040,13 +15021,6 @@ "node": ">=12" } }, - "node_modules/proxy-agent/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT", - "optional": true - }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -15143,6 +15117,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quickjs-wasi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-2.2.0.tgz", + "integrity": "sha512-zQxXmQMrEoD3S+jQdYsloq4qAuaxKFHZj6hHqOYGwB2iQZH+q9e/lf5zQPXCKOk0WJuAjzRFbO4KwHIp2D05Iw==", + "license": "MIT", + "optional": true + }, "node_modules/quickselect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", @@ -16357,18 +16338,18 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-10.0.0.tgz", + "integrity": "sha512-pyp2YR3mNxAMu0mGLtzs4g7O3uT4/9sQOLAKcViAkaS9fJWkud7nmaf6ZREFqQEi24IPkBcjfHjXhPTUWjo3uA==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", + "agent-base": "9.0.0", "debug": "^4.3.4", "socks": "^2.8.3" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/sonic-boom": { @@ -17956,9 +17937,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -18071,9 +18052,9 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "license": "MIT" }, "node_modules/unicorn-magic": { @@ -19199,10 +19180,10 @@ "license": "MIT", "dependencies": { "@goauthentik/tsconfig": "^1.0.9", - "@types/node": "^25.5.2", + "@types/node": "^25.6.0", "@types/semver": "^7.7.1", "semver": "^7.7.4", - "typescript": "^6.0.2" + "typescript": "^6.0.3" }, "engines": { "node": ">=24", @@ -19244,7 +19225,7 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-swc": "^0.4.0", "@swc/cli": "^0.8.1", - "@swc/core": "^1.15.24", + "@swc/core": "^1.15.30", "base64-js": "^1.5.1", "core-js": "^3.49.0", "formdata-polyfill": "^2025.11.0", diff --git a/web/package.json b/web/package.json index 989e2bb970..63f7f49636 100644 --- a/web/package.json +++ b/web/package.json @@ -97,7 +97,7 @@ "@codemirror/theme-one-dark": "^6.1.3", "@eslint/js": "^9.39.3", "@floating-ui/dom": "^1.7.6", - "@formatjs/intl-listformat": "^8.3.1", + "@formatjs/intl-listformat": "^8.3.2", "@fortawesome/fontawesome-free": "^7.2.0", "@goauthentik/api": "0.0.0", "@goauthentik/core": "^1.0.0", @@ -119,7 +119,7 @@ "@patternfly/elements": "^4.3.1", "@patternfly/patternfly": "^4.224.2", "@playwright/test": "^1.59.1", - "@sentry/browser": "^10.47.0", + "@sentry/browser": "^10.48.0", "@storybook/addon-docs": "^10.3.5", "@storybook/addon-links": "^10.3.5", "@storybook/web-components": "^10.3.5", @@ -127,7 +127,7 @@ "@types/codemirror": "^5.60.17", "@types/grecaptcha": "^3.0.9", "@types/guacamole-common-js": "^1.5.5", - "@types/node": "^25.5.2", + "@types/node": "^25.6.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^8.57.2", @@ -151,10 +151,10 @@ "eslint-plugin-lit": "^2.2.1", "eslint-plugin-wc": "^3.1.0", "fuse.js": "^7.3.0", - "globals": "^17.4.0", + "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "hastscript": "^9.0.1", - "knip": "^6.3.1", + "knip": "^6.4.1", "lex": "^2025.11.0", "lit": "^3.3.2", "lit-analyzer": "^2.0.3", @@ -167,7 +167,7 @@ "pino": "^10.3.1", "pino-pretty": "^13.1.2", "playwright": "^1.58.2", - "prettier": "^3.8.2", + "prettier": "^3.8.3", "prettier-plugin-packagejson": "^3.0.2", "pseudolocale": "^2.2.0", "rapidoc": "^9.3.8", @@ -187,7 +187,7 @@ "ts-pattern": "^5.9.0", "turnstile-types": "^1.2.3", "type-fest": "^5.5.0", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.57.2", "unist-util-visit": "^5.1.0", "vite": "^8.0.8", @@ -203,7 +203,7 @@ "@rollup/rollup-darwin-arm64": "^4.57.1", "@rollup/rollup-linux-arm64-gnu": "^4.57.1", "@rollup/rollup-linux-x64-gnu": "^4.57.1", - "chromedriver": "^147.0.2" + "chromedriver": "^147.0.4" }, "workspaces": [ "./packages/*" diff --git a/web/packages/core/package.json b/web/packages/core/package.json index 707afeb302..14c608276e 100644 --- a/web/packages/core/package.json +++ b/web/packages/core/package.json @@ -45,10 +45,10 @@ }, "dependencies": { "@goauthentik/tsconfig": "^1.0.9", - "@types/node": "^25.5.2", + "@types/node": "^25.6.0", "@types/semver": "^7.7.1", "semver": "^7.7.4", - "typescript": "^6.0.2" + "typescript": "^6.0.3" }, "engines": { "node": ">=24", diff --git a/web/packages/sfe/package.json b/web/packages/sfe/package.json index c5ed2f80a4..e121dce1cc 100644 --- a/web/packages/sfe/package.json +++ b/web/packages/sfe/package.json @@ -20,7 +20,7 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-swc": "^0.4.0", "@swc/cli": "^0.8.1", - "@swc/core": "^1.15.24", + "@swc/core": "^1.15.30", "base64-js": "^1.5.1", "core-js": "^3.49.0", "formdata-polyfill": "^2025.11.0", diff --git a/web/src/admin/admin-settings/AdminSettingsFooterLinks.ts b/web/src/admin/admin-settings/AdminSettingsFooterLinks.ts index eb36949fb5..bd0f96f67c 100644 --- a/web/src/admin/admin-settings/AdminSettingsFooterLinks.ts +++ b/web/src/admin/admin-settings/AdminSettingsFooterLinks.ts @@ -53,7 +53,7 @@ export class FooterLinkInput extends AKControlElement { } get valid() { - const href = this.json()?.href ?? ""; + const href = this.toJSON()?.href ?? ""; return hasLegalScheme(href) && URL.canParse(href); } diff --git a/web/src/admin/admin-settings/stories/AdminSettingsFooterLinks.stories.ts b/web/src/admin/admin-settings/stories/AdminSettingsFooterLinks.stories.ts index 79ecccdcbc..5776c1454d 100644 --- a/web/src/admin/admin-settings/stories/AdminSettingsFooterLinks.stories.ts +++ b/web/src/admin/admin-settings/stories/AdminSettingsFooterLinks.stories.ts @@ -34,7 +34,7 @@ const metadata: Meta = { return; } const target = event.target as FooterLinkInput; - messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`; + messages!.innerText = `${JSON.stringify(target.toJSON(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`; }); }, 250); diff --git a/web/src/admin/admin-settings/stories/ak-array-input.stories.ts b/web/src/admin/admin-settings/stories/ak-array-input.stories.ts index c3375bd89a..da9c995d8b 100644 --- a/web/src/admin/admin-settings/stories/ak-array-input.stories.ts +++ b/web/src/admin/admin-settings/stories/ak-array-input.stories.ts @@ -40,7 +40,7 @@ const metadata: Meta> = { return; } const target = event.target as FooterLinkInput; - messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`; + messages!.innerText = `${JSON.stringify(target.toJSON(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`; }); }, 250); diff --git a/web/src/admin/ak-about-modal.ts b/web/src/admin/ak-about-modal.ts index 52f9461d03..59f78395ee 100644 --- a/web/src/admin/ak-about-modal.ts +++ b/web/src/admin/ak-about-modal.ts @@ -8,7 +8,7 @@ import { AKModal } from "#elements/dialogs/ak-modal"; import { WithBrandConfig } from "#elements/mixins/branding"; import { WithLicenseSummary } from "#elements/mixins/license"; import { SlottedTemplateResult } from "#elements/types"; -import { ThemedImage } from "#elements/utils/images"; +import { DefaultFlowBackground, ThemedImage } from "#elements/utils/images"; import { AdminApi, @@ -27,8 +27,6 @@ import { customElement, state } from "lit/decorators.js"; import PFAbout from "@patternfly/patternfly/components/AboutModalBox/about-modal-box.css"; -const DEFAULT_BRAND_IMAGE = "/static/dist/assets/images/flow_background.jpg"; - type AboutEntry = [label: string, content?: SlottedTemplateResult]; function renderEntry([label, content = null]: AboutEntry): SlottedTemplateResult { @@ -191,7 +189,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) { ${ref(this.scrollContainerRef)} class="pf-c-about-modal-box" style=${styleMap({ - "--pf-c-about-modal-box__hero--sm--BackgroundImage": `url(${DEFAULT_BRAND_IMAGE})`, + "--pf-c-about-modal-box__hero--sm--BackgroundImage": `url(${DefaultFlowBackground})`, })} part="box" > diff --git a/web/src/admin/applications/ApplicationCheckAccessForm.ts b/web/src/admin/applications/ApplicationCheckAccessForm.ts index 33c564ace3..df7c4a5aa5 100644 --- a/web/src/admin/applications/ApplicationCheckAccessForm.ts +++ b/web/src/admin/applications/ApplicationCheckAccessForm.ts @@ -4,8 +4,12 @@ import "#elements/forms/HorizontalFormElement"; import "#elements/forms/SearchSelect/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { PFSize } from "#common/enums"; +import { APIMessage, MessageLevel } from "#common/messages"; import { Form } from "#elements/forms/Form"; +import { SlottedTemplateResult } from "#elements/types"; +import { ifPresent } from "#elements/utils/attributes"; import { Application, @@ -16,13 +20,24 @@ import { } from "@goauthentik/api"; import { msg } from "@lit/localize"; -import { CSSResult, html, nothing, TemplateResult } from "lit"; +import { CSSResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; @customElement("ak-application-check-access-form") export class ApplicationCheckAccessForm extends Form<{ forUser: number }> { + public static override verboseName = msg("Access"); + public static override submitVerb = msg("Check"); + public static override createLabel = msg("Check"); + public static override submittingVerb = msg("Checking"); + + static styles: CSSResult[] = [...super.styles, PFDescriptionList]; + + #api = new CoreApi(DEFAULT_CONFIG); + + public override size = PFSize.XLarge; + @property({ attribute: false }) public application!: Application; @@ -30,19 +45,27 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> { public result: PolicyTestResult | null = null; @property({ attribute: false }) - public request?: number; + public request: number | null = null; - getSuccessMessage(): string { - return msg("Successfully sent test-request."); + public override formatAPISuccessMessage(): APIMessage { + return { + level: MessageLevel.success, + message: msg("Successfully sent test-request."), + }; } - async send(data: { forUser: number }): Promise { + protected override send(data: { forUser: number }): Promise { this.request = data.forUser; - const result = await new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({ - slug: this.application?.slug, - forUser: data.forUser, - }); - return (this.result = result); + + return this.#api + .coreApplicationsCheckAccessRetrieve({ + slug: this.application?.slug, + forUser: data.forUser, + }) + .then((result) => { + this.result = result; + return result; + }); } public override reset(): void { @@ -50,15 +73,14 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> { this.result = null; } - static styles: CSSResult[] = [...super.styles, PFDescriptionList]; + protected renderResult(): SlottedTemplateResult { + const { passing, messages = [], logMessages = [] } = this.result || {}; - renderResult(): TemplateResult { - return html` - + return html`
- +
@@ -67,54 +89,56 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
    - ${(this.result?.messages || []).length > 0 - ? this.result?.messages?.map((m) => { - return html`
  • - ${m} -
  • `; - }) - : html`
  • - - -
  • `} + ${messages.map((m) => { + return html`
  • + ${m} +
  • `; + })}
- - - `; + +
`; } - protected override renderForm(): TemplateResult { + protected override renderForm(): SlottedTemplateResult { return html` => { const args: CoreUsersListRequest = { ordering: "username", }; - if (query !== undefined) { + + if (query) { args.search = query; } - const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); + + const users = await this.#api.coreUsersList(args); + return users.results; }} .renderElement=${(user: User): string => { return user.username; }} - .renderDescription=${(user: User): TemplateResult => { + .renderDescription=${(user: User): SlottedTemplateResult => { return html`${user.name}`; }} .value=${(user: User | undefined): number | undefined => { return user?.pk; }} .selected=${(user: User): boolean => { - return user.pk.toString() === this.request?.toString(); + return ( + typeof this.request === "number" && + user.pk.toString() === this.request.toString() + ); }} > - ${this.result ? this.renderResult() : nothing}`; + ${this.renderResult()}`; } } diff --git a/web/src/admin/applications/ApplicationListPage.css b/web/src/admin/applications/ApplicationListPage.css index 98a6e4ea5d..a78a7def9f 100644 --- a/web/src/admin/applications/ApplicationListPage.css +++ b/web/src/admin/applications/ApplicationListPage.css @@ -1,8 +1,3 @@ -/* Fix alignment issues with images in tables */ -.pf-c-table tbody > tr > * { - vertical-align: middle; -} - tr td:first-child { width: auto; min-width: 0px; diff --git a/web/src/admin/applications/ApplicationListPage.ts b/web/src/admin/applications/ApplicationListPage.ts index aebb4c6390..f888e8351c 100644 --- a/web/src/admin/applications/ApplicationListPage.ts +++ b/web/src/admin/applications/ApplicationListPage.ts @@ -7,7 +7,6 @@ import "#elements/forms/DeleteBulkForm"; import "#elements/forms/ModalForm"; import "#elements/dialogs/ak-modal"; import "#admin/applications/ApplicationForm"; -import "#admin/applications/ApplicationWizardHint"; import { DEFAULT_CONFIG } from "#common/api/config"; @@ -127,6 +126,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage) return [ html`) "Opens the new application wizard, which will guide you through creating a new application with an existing provider.", )} > - ${msg("With New Provider...")} + ${msg("with New Provider...")}
  • @@ -214,7 +214,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage) "Opens the new application form, which will guide you through creating a new application with an existing provider.", )} > - ${msg("With Existing Provider...")} + ${msg("with Existing Provider...")}
  • diff --git a/web/src/admin/applications/ApplicationViewPage.ts b/web/src/admin/applications/ApplicationViewPage.ts index 3a3f50e389..014c956f49 100644 --- a/web/src/admin/applications/ApplicationViewPage.ts +++ b/web/src/admin/applications/ApplicationViewPage.ts @@ -16,11 +16,15 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { WithLicenseSummary } from "#elements/mixins/license"; import { setPageDetails } from "#components/ak-page-navbar"; import renderDescriptionList from "#components/DescriptionList"; +import { ApplicationCheckAccessForm } from "#admin/applications/ApplicationCheckAccessForm"; +import { ApplicationForm } from "#admin/applications/ApplicationForm"; + import { Application, ContentTypeEnum, @@ -181,36 +185,28 @@ export class ApplicationViewPage extends WithLicenseSummary(AKElement) { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update Application")} - - - - - - ${msg("Check")} - ${msg("Check Application access")} - - - - + html` + ${this.application.launchUrl ? html` ${msg("Launch")} ` - : nothing}`, + : null}`, ], ])} diff --git a/web/src/admin/applications/ApplicationWizardHint.ts b/web/src/admin/applications/ApplicationWizardHint.ts deleted file mode 100644 index df09084fde..0000000000 --- a/web/src/admin/applications/ApplicationWizardHint.ts +++ /dev/null @@ -1,134 +0,0 @@ -import "#admin/applications/wizard/ak-application-wizard"; -import "#components/ak-hint/ak-hint"; -import "#components/ak-hint/ak-hint-body"; -import "#elements/Label"; -import "#elements/buttons/ActionButton/ak-action-button"; - -import { AKElement } from "#elements/Base"; -import { getURLParam } from "#elements/router/RouteMatch"; - -import { ShowHintController, ShowHintControllerHost } from "#components/ak-hint/ShowHintController"; - -import { msg } from "@lit/localize"; -import { css, html } from "lit"; -import { customElement, property, state } from "lit/decorators.js"; -import { styleMap } from "lit/directives/style-map.js"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFLabel from "@patternfly/patternfly/components/Label/label.css"; -import PFPage from "@patternfly/patternfly/components/Page/page.css"; - -const closeButtonIcon = html``; - -@customElement("ak-application-wizard-hint") -export class AkApplicationWizardHint extends AKElement implements ShowHintControllerHost { - static styles = [ - PFButton, - PFPage, - PFLabel, - css` - .pf-c-page__main-section { - padding-bottom: 0; - } - .ak-hint-text { - padding-bottom: var(--pf-global--spacer--md); - } - `, - ]; - - @property({ type: Boolean, attribute: "show-hint" }) - forceHint: boolean = false; - - @state() - showHint: boolean = true; - - showHintController: ShowHintController; - - constructor() { - super(); - this.showHintController = new ShowHintController( - this, - "202310-application-wizard-announcement", - ); - } - - renderReminder() { - const sectionStyles = { - paddingBottom: "0", - marginBottom: "-0.5rem", - marginRight: "0.0625rem", - textAlign: "right", - }; - const textStyle = { maxWidth: "60ch" }; - - return html`
    - - - - ${msg("One hint, 'New Application Wizard', is currently hidden")} - - - - -
    `; - } - - renderHint() { - return html`
    - - -

    - You can now configure both an application and its authentication provider at - the same time with our new Application Wizard. - -

    - - - -
    - ${this.showHintController.render()} -
    -
    `; - } - - render() { - return this.showHint || this.forceHint ? this.renderHint() : this.renderReminder(); - } -} - -export default AkApplicationWizardHint; - -declare global { - interface HTMLElementTagNameMap { - "ak-application-wizard-hint": AkApplicationWizardHint; - } -} diff --git a/web/src/admin/applications/ak-provider-table.ts b/web/src/admin/applications/ak-provider-table.ts index c9e3b8e4ad..1942bcccbb 100644 --- a/web/src/admin/applications/ak-provider-table.ts +++ b/web/src/admin/applications/ak-provider-table.ts @@ -8,7 +8,6 @@ import { SlottedTemplateResult } from "#elements/types"; import { Provider, ProvidersApi } from "@goauthentik/api"; import { msg } from "@lit/localize"; -import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("ak-provider-table") @@ -23,29 +22,24 @@ export class ProviderTable extends Table { public override order = "name"; - protected async apiEndpoint(): Promise> { + protected override async apiEndpoint(): Promise> { return new ProvidersApi(DEFAULT_CONFIG).providersAllList({ ...(await this.defaultEndpointConfig()), backchannel: this.backchannel, }); } - protected columns: TableColumn[] = [ + protected override columns: TableColumn[] = [ // --- [msg("Name"), "username"], [msg("Type")], ]; - protected row(item: Provider): SlottedTemplateResult[] { - return [ - html`
    -
    ${item.name}
    -
    `, - html`${item.verboseName}`, - ]; + protected override row(item: Provider): SlottedTemplateResult[] { + return [item.name, item.verboseName]; } - protected renderSelectedChip(item: Provider): SlottedTemplateResult { + protected override renderSelectedChip(item: Provider): SlottedTemplateResult { return item.name; } } diff --git a/web/src/admin/applications/components/ak-backchannel-input.ts b/web/src/admin/applications/components/ak-backchannel-input.ts index 132dfbe597..c0205993e8 100644 --- a/web/src/admin/applications/components/ak-backchannel-input.ts +++ b/web/src/admin/applications/components/ak-backchannel-input.ts @@ -73,7 +73,6 @@ export class AkBackchannelProvidersInput extends AKElement { }} > ${this.help ? html`

    ${this.help}

    ` : nothing} - `); @@ -100,7 +99,11 @@ export class AkBackchannelProvidersInput extends AKElement {
    - ${map(this.providers, renderOneChip)} + ${map(this.providers, renderOneChip)}
    ${this.help ? html`

    ${this.help}

    ` : nothing} diff --git a/web/src/admin/applications/wizard/steps/ak-application-wizard-bindings-step.ts b/web/src/admin/applications/wizard/steps/ak-application-wizard-bindings-step.ts index ad874f1e1e..ba5c947a56 100644 --- a/web/src/admin/applications/wizard/steps/ak-application-wizard-bindings-step.ts +++ b/web/src/admin/applications/wizard/steps/ak-application-wizard-bindings-step.ts @@ -96,7 +96,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep { protected onDeleteBindings() { const toDelete = this.selectTable - .json() + .toJSON() .map((i) => (typeof i === "string" ? parseInt(i, 10) : i)); const bindings = this.wizard.bindings.filter((binding, index) => !toDelete.includes(index)); diff --git a/web/src/admin/brands/BrandForm.ts b/web/src/admin/brands/BrandForm.ts index 7590cf73ad..02fc0c0dc1 100644 --- a/web/src/admin/brands/BrandForm.ts +++ b/web/src/admin/brands/BrandForm.ts @@ -14,6 +14,7 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { DefaultBrand } from "#common/ui/config"; import { ModelForm } from "#elements/forms/ModelForm"; +import { DefaultFlowBackground } from "#elements/utils/images"; import { AKLabel } from "#components/ak-label"; @@ -65,7 +66,17 @@ export class BrandForm extends ModelForm { } protected override renderForm(): TemplateResult { - return html` { help=${msg( "Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match.", )} + ?autofocus=${!this.instance} > { required name="brandingTitle" placeholder="authentik" - value="${this.instance?.brandingTitle ?? DefaultBrand.brandingTitle}" + value=${brandingTitle} label=${msg("Title")} autocomplete="off" spellcheck="false" @@ -102,7 +114,7 @@ export class BrandForm extends ModelForm { required name="brandingLogo" label=${msg("Logo")} - value="${this.instance?.brandingLogo ?? DefaultBrand.brandingLogo}" + value=${brandingLogo} .usage=${UsageEnum.Media} help=${msg("Logo shown in sidebar/header and flow executor.")} > @@ -111,7 +123,7 @@ export class BrandForm extends ModelForm { required name="brandingFavicon" label=${msg("Favicon")} - value="${this.instance?.brandingFavicon ?? DefaultBrand.brandingFavicon}" + value=${brandingFavicon} .usage=${UsageEnum.Media} help=${msg("Icon shown in the browser tab.")} > @@ -120,8 +132,7 @@ export class BrandForm extends ModelForm { required name="brandingDefaultFlowBackground" label=${msg("Default flow background")} - value="${this.instance?.brandingDefaultFlowBackground ?? - "/static/dist/assets/images/flow_background.jpg"}" + value=${defaultFlowBackground} .usage=${UsageEnum.Media} help=${msg( "Default background used during flow execution. Can be overridden per flow.", @@ -141,8 +152,7 @@ export class BrandForm extends ModelForm {

    diff --git a/web/src/admin/endpoints/connectors/ConnectorWizard.ts b/web/src/admin/endpoints/connectors/ConnectorWizard.ts index 207169c60d..4f99fc578d 100644 --- a/web/src/admin/endpoints/connectors/ConnectorWizard.ts +++ b/web/src/admin/endpoints/connectors/ConnectorWizard.ts @@ -10,6 +10,7 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { SlottedTemplateResult } from "#elements/types"; import { CreateWizard } from "#elements/wizard/CreateWizard"; +import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage"; import { EndpointsApi, TypeCreate } from "@goauthentik/api"; @@ -23,6 +24,8 @@ export class AKEndpointConnectorWizard extends CreateWizard { public static override verboseName = msg("Endpoint Connector"); public static override verboseNamePlural = msg("Endpoint Connectors"); + public override layout = TypeCreateWizardPageLayouts.grid; + protected apiEndpoint = (requestInit?: RequestInit): Promise => { return this.#api.endpointsConnectorsTypesList(requestInit); }; diff --git a/web/src/admin/endpoints/devices/DeviceForm.ts b/web/src/admin/endpoints/devices/DeviceForm.ts index 0b34fea8d1..67b56ba4cc 100644 --- a/web/src/admin/endpoints/devices/DeviceForm.ts +++ b/web/src/admin/endpoints/devices/DeviceForm.ts @@ -20,6 +20,8 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-endpoints-device-form") export class EndpointDeviceForm extends ModelForm { + public static override verboseName = msg("Device"); + public static override verboseNamePlural = msg("Devices"); loadInstance(pk: string): Promise { return new EndpointsApi(DEFAULT_CONFIG).endpointsDevicesRetrieve({ deviceUuid: pk, diff --git a/web/src/admin/endpoints/devices/DeviceListPage.ts b/web/src/admin/endpoints/devices/DeviceListPage.ts index 3131ab2da0..2f13cb2bca 100644 --- a/web/src/admin/endpoints/devices/DeviceListPage.ts +++ b/web/src/admin/endpoints/devices/DeviceListPage.ts @@ -1,15 +1,16 @@ import "#elements/cards/AggregateCard"; import "#elements/forms/DeleteBulkForm"; -import "#admin/endpoints/devices/DeviceForm"; import "#admin/endpoints/devices/DeviceAddHowTo"; -import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { modalInvoker } from "#elements/dialogs"; import { PaginatedResponse, TableColumn, Timestamp } from "#elements/table/Table"; import { TablePage } from "#elements/table/TablePage"; import { SlottedTemplateResult } from "#elements/types"; +import { EndpointDeviceForm } from "#admin/endpoints/devices/DeviceForm"; + import { DeviceSummary, EndpointDevice, EndpointsApi } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -132,17 +133,14 @@ export class DeviceListPage extends TablePage { html`${item.facts.data.os?.name} ${item.facts.data.os?.version}`, html`${item.accessGroupObj?.name || "-"}`, item.facts.created ? Timestamp(item.facts.created) : html`-`, - html` - ${msg("Save Changes")} - ${msg("Update Device")} - - - - `, + html``, ]; } diff --git a/web/src/admin/endpoints/devices/DeviceViewPage.ts b/web/src/admin/endpoints/devices/DeviceViewPage.ts index 6f08c2e0ae..dc29ed4e62 100644 --- a/web/src/admin/endpoints/devices/DeviceViewPage.ts +++ b/web/src/admin/endpoints/devices/DeviceViewPage.ts @@ -5,20 +5,20 @@ import "#admin/endpoints/devices/facts/DeviceProcessTable"; import "#admin/endpoints/devices/facts/DeviceUserTable"; import "#admin/endpoints/devices/facts/DeviceSoftwareTable"; import "#admin/endpoints/devices/facts/DeviceGroupTable"; -import "#admin/endpoints/devices/DeviceForm"; import "#admin/endpoints/devices/DeviceEvents"; -import "#elements/forms/ModalForm"; import "#elements/Tabs"; import { DEFAULT_CONFIG } from "#common/api/config"; import { APIError, parseAPIResponseError } from "#common/errors/network"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { Timestamp } from "#elements/table/shared"; import { setPageDetails } from "#components/ak-page-navbar"; import renderDescriptionList, { DescriptionPair } from "#components/DescriptionList"; +import { EndpointDeviceForm } from "#admin/endpoints/devices/DeviceForm"; import { getSize, osFamilyToLabel, trySortNumerical } from "#admin/endpoints/devices/utils"; import { DeviceConnection, Disk, EndpointDeviceDetails, EndpointsApi } from "@goauthentik/api"; @@ -124,18 +124,14 @@ export class DeviceViewPage extends AKElement { [msg("Device access group"), this.device?.accessGroupObj?.name ?? "-"], [ msg("Actions"), - html` - ${msg("Save Changes")} - ${msg("Update Device")} - - - - `, + html``, ], ], { horizontal: true }, diff --git a/web/src/admin/flows/BoundStagesList.ts b/web/src/admin/flows/BoundStagesList.ts index 6f317d38d2..b709787357 100644 --- a/web/src/admin/flows/BoundStagesList.ts +++ b/web/src/admin/flows/BoundStagesList.ts @@ -20,22 +20,24 @@ import { AKStageWizard } from "#admin/stages/ak-stage-wizard"; import { FlowsApi, FlowStageBinding, ModelEnum } from "@goauthentik/api"; import { msg } from "@lit/localize"; -import { html, TemplateResult } from "lit"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("ak-bound-stages-list") export class BoundStagesList extends Table { - expandable = true; - checkbox = true; - clearOnRefresh = true; + protected flowsAPI = new FlowsApi(DEFAULT_CONFIG); - order = "order"; + public override expandable = true; + public override checkbox = true; + public override clearOnRefresh = true; - @property() - target?: string; + public override order = "order"; - async apiEndpoint(): Promise> { - return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({ + @property({ type: String, useDefault: true }) + public target: string | null = null; + + protected override async apiEndpoint(): Promise> { + return this.flowsAPI.flowsBindingsList({ ...(await this.defaultEndpointConfig()), target: this.target || "", }); @@ -52,7 +54,7 @@ export class BoundStagesList extends Table { [msg("Actions"), null, msg("Row Actions")], ]; - renderToolbarSelected(): TemplateResult { + renderToolbarSelected(): SlottedTemplateResult { const disabled = this.selectedElements.length < 1; return html` { ]; }} .usedBy=${(item: FlowStageBinding) => { - return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUsedByList({ + return this.flowsAPI.flowsBindingsUsedByList({ fsbUuid: item.pk, }); }} .delete=${(item: FlowStageBinding) => { - return new FlowsApi(DEFAULT_CONFIG).flowsBindingsDestroy({ + return this.flowsAPI.flowsBindingsDestroy({ fsbUuid: item.pk, }); }} @@ -80,7 +82,7 @@ export class BoundStagesList extends Table { `; } - row(item: FlowStageBinding): SlottedTemplateResult[] { + protected override row(item: FlowStageBinding): SlottedTemplateResult[] { return [ html`

    ${item.order}
    `, item.stageObj?.name, @@ -115,30 +117,27 @@ export class BoundStagesList extends Table { protected renderActions(): SlottedTemplateResult { return html` - `; + class="pf-c-button pf-m-primary" + ${modalInvoker(AKStageWizard, { + showBindingPage: true, + bindingTarget: this.target, + })} + > + ${msg("Create or bind...")} + `; } - protected override renderExpanded(item: FlowStageBinding): TemplateResult { + protected override renderExpanded(item: FlowStageBinding): SlottedTemplateResult { return html`
    -

    ${msg("These bindings control if this stage will be applied to the flow.")}

    + ${msg( + "These bindings control if this stage will be applied to the flow.", + )}
    `; } diff --git a/web/src/admin/flows/FlowForm.ts b/web/src/admin/flows/FlowForm.ts index ee45e937c0..f9418abb8a 100644 --- a/web/src/admin/flows/FlowForm.ts +++ b/web/src/admin/flows/FlowForm.ts @@ -218,6 +218,15 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm) { > ${msg("Require Outpost (flow can only be executed from an outpost)")} +

    ${msg("Required authentication level for this flow.")} diff --git a/web/src/admin/flows/FlowViewPage.ts b/web/src/admin/flows/FlowViewPage.ts index 7d68a70502..1d3bd92cab 100644 --- a/web/src/admin/flows/FlowViewPage.ts +++ b/web/src/admin/flows/FlowViewPage.ts @@ -1,6 +1,5 @@ import "#admin/flows/BoundStagesList"; import "#admin/flows/FlowDiagram"; -import "#admin/flows/FlowForm"; import "#admin/policies/BoundPoliciesList"; import "#admin/rbac/ak-rbac-object-permission-page"; import "#admin/events/ObjectChangelog"; @@ -11,11 +10,13 @@ import { AndNext, DEFAULT_CONFIG } from "#common/api/config"; import { isResponseErrorLike } from "#common/errors/network"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { SlottedTemplateResult } from "#elements/types"; import { setPageDetails } from "#components/ak-page-navbar"; import renderDescriptionList from "#components/DescriptionList"; +import { FlowForm } from "#admin/flows/FlowForm"; import { DesignationToLabel } from "#admin/flows/utils"; import { Flow, FlowsApi, ModelEnum } from "@goauthentik/api"; @@ -97,21 +98,14 @@ export class FlowViewPage extends AKElement { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update Flow")} - - - - + html` - ${msg("Save Changes")} - ${msg("Update Group")} - - - - `, + html``, ], ])} diff --git a/web/src/admin/groups/RelatedGroupList.ts b/web/src/admin/groups/RelatedGroupList.ts index 37dd799c92..f773d78ded 100644 --- a/web/src/admin/groups/RelatedGroupList.ts +++ b/web/src/admin/groups/RelatedGroupList.ts @@ -1,19 +1,19 @@ -import "#admin/groups/ak-group-form"; import "#admin/users/ak-user-group-table"; import "#components/ak-status-label"; import "#elements/buttons/SpinnerButton/index"; import "#elements/forms/DeleteBulkForm"; import "#elements/forms/HorizontalFormElement"; -import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; -import { renderModal } from "#elements/dialogs"; +import { modalInvoker, renderModal } from "#elements/dialogs"; import { AKFormSubmitEvent, Form } from "#elements/forms/Form"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; +import { GroupForm } from "#admin/groups/ak-group-form"; + import { CoreApi, Group, User } from "@goauthentik/api"; import { msg, str } from "@lit/localize"; @@ -23,6 +23,10 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-group-related-add") export class RelatedGroupAdd extends Form<{ groups: string[] }> { + public static override verboseName = msg("Group"); + public static override submitVerb = msg("Add"); + public static override createLabel = msg("Add"); + @property({ attribute: false }) public user?: User; @@ -74,7 +78,10 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {

    - + ${this.groupsToAdd.map((group) => { return html` { return [ html`${item.name}`, html``, - html` - ${msg("Save Changes")} - ${msg("Update Group")} - - - `, + html``, ]; } renderToolbar(): TemplateResult { return html` ${this.targetUser - ? html` - ${msg("Add")} - ${msg("Add Group")} - - - - ` + ? html`` : nothing} - - ${msg("Create")} - ${msg("Create Group")} - - - + ${super.renderToolbar()} `; } diff --git a/web/src/admin/groups/RelatedUserList.ts b/web/src/admin/groups/RelatedUserList.ts index 7eb9c37764..9a71ce90cc 100644 --- a/web/src/admin/groups/RelatedUserList.ts +++ b/web/src/admin/groups/RelatedUserList.ts @@ -14,6 +14,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatDisambiguatedUserDisplayName } from "#common/users"; import { IconEditButton, renderModal } from "#elements/dialogs"; import { AKFormSubmitEvent, Form } from "#elements/forms/Form"; @@ -22,11 +23,11 @@ import { WithCapabilitiesConfig } from "#elements/mixins/capabilities"; import { getURLParam, updateURLParams } from "#elements/router/RouteMatch"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; -import { UserOption } from "#elements/user/utils"; import { AKLabel } from "#components/ak-label"; import { RecoveryButtons } from "#admin/users/recovery"; +import { ToggleUserActivationButton } from "#admin/users/UserActiveForm"; import { UserForm } from "#admin/users/UserForm"; import { UserImpersonateForm } from "#admin/users/UserImpersonateForm"; @@ -153,7 +154,7 @@ export class AddRelatedUserForm extends Form<{ users: number[] }> { this.requestUpdate(); }} > - ${UserOption(user)} + ${formatDisambiguatedUserDisplayName(user)} `; })} @@ -317,22 +318,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
    - { - return new CoreApi(DEFAULT_CONFIG).coreUsersPartialUpdate({ - id: item.pk || 0, - patchedUserRequest: { - isActive: !item.isActive, - }, - }); - }} - > - - + ${ToggleUserActivationButton(item)}
    diff --git a/web/src/admin/policies/BoundPoliciesList.ts b/web/src/admin/policies/BoundPoliciesList.ts index c159cedbcb..bf6020568a 100644 --- a/web/src/admin/policies/BoundPoliciesList.ts +++ b/web/src/admin/policies/BoundPoliciesList.ts @@ -184,7 +184,7 @@ export class BoundPoliciesList extends bindingTarget: this.target, })} > - ${msg("Create and bind Policy")} + ${msg("Create or bind...")} `; } @@ -223,44 +223,16 @@ export class BoundPoliciesList extends html`${msg("No Policies bound.")}
    ${msg("No policies are currently bound to this object.")}
    -
    +
    ${msg("Policy actions")} ${this.renderNewPolicyButton()} - -
    +
    `, ); } renderToolbar(): SlottedTemplateResult { - return html`${this.allowedTypes.includes(PolicyBindingCheckTarget.Policy) - ? this.renderNewPolicyButton() - : null} - `; + return this.renderNewPolicyButton(); } renderPolicyEngineMode() { @@ -270,10 +242,15 @@ export class BoundPoliciesList extends if (policyEngineMode === undefined) { return nothing; } - return html`

    - ${msg(str`The currently selected policy engine mode is ${policyEngineMode.label}:`)} - ${policyEngineMode.description} -

    `; + return html`${this.findSlotted("description") + ? html`

    + +

    ` + : nothing} +

    + ${msg(str`The currently selected policy engine mode is ${policyEngineMode.label}:`)} + ${policyEngineMode.description} +

    `; } renderToolbarContainer(): SlottedTemplateResult { diff --git a/web/src/admin/policies/PolicyBindingForm.ts b/web/src/admin/policies/PolicyBindingForm.ts index ed6338f702..1fdd6d23b4 100644 --- a/web/src/admin/policies/PolicyBindingForm.ts +++ b/web/src/admin/policies/PolicyBindingForm.ts @@ -63,7 +63,7 @@ export class PolicyBindingForm extends public targetPk = ""; @state() - protected policyGroupUser: PolicyBindingCheckTarget = PolicyBindingCheckTarget.Policy; + public policyGroupUser: PolicyBindingCheckTarget = PolicyBindingCheckTarget.Policy; @property({ type: Array }) public allowedTypes: PolicyBindingCheckTarget[] = [ @@ -161,107 +161,109 @@ export class PolicyBindingForm extends `; } + protected renderTarget() { + return html` + { + return groupBy(items, (policy) => policy.verboseNamePlural); + }} + .fetchObjects=${async (query?: string): Promise => { + const args: PoliciesAllListRequest = { + ordering: "name", + }; + if (query !== undefined) { + args.search = query; + } + const policies = await new PoliciesApi(DEFAULT_CONFIG).policiesAllList( + args, + ); + return policies.results; + }} + .renderElement=${(policy: Policy) => policy.name} + .value=${(policy: Policy | null) => policy?.pk} + .selected=${(policy: Policy) => policy.pk === this.instance?.policy} + blankable + > + + ${this.typeNotices + .filter(({ type }) => type === PolicyBindingCheckTarget.Policy) + .map((msg) => { + return html`

    ${msg.notice}

    `; + })} +
    + + => { + const args: CoreGroupsListRequest = { + ordering: "name", + includeUsers: false, + }; + if (query !== undefined) { + args.search = query; + } + const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(args); + return groups.results; + }} + .renderElement=${(group: Group): string => { + return group.name; + }} + .value=${(group: Group | null) => String(group?.pk ?? "")} + .selected=${(group: Group) => group.pk === this.instance?.group} + blankable + > + + ${this.typeNotices + .filter(({ type }) => type === PolicyBindingCheckTarget.Group) + .map((msg) => { + return html`

    ${msg.notice}

    `; + })} +
    + + => { + const args: CoreUsersListRequest = { + ordering: "username", + }; + if (query !== undefined) { + args.search = query; + } + const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); + return users.results; + }} + .renderElement=${(user: User) => user.username} + .renderDescription=${(user: User) => html`${user.name}`} + .value=${(user: User | null) => user?.pk} + .selected=${(user: User) => user.pk === this.instance?.user} + blankable + > + + ${this.typeNotices + .filter(({ type }) => type === PolicyBindingCheckTarget.User) + .map((msg) => { + return html`

    ${msg.notice}

    `; + })} +
    `; + } + protected override renderForm(): TemplateResult { - return html`
    -
    ${this.renderModeSelector()}
    - -
    + return html`${this.allowedTypes.length > 1 + ? html`
    +
    ${this.renderModeSelector()}
    + +
    ` + : this.renderTarget()} => { - return this.#api.policiesAllTypesList(requestInit); + protected override apiEndpoint = async (requestInit?: RequestInit): Promise => { + return this.policiesAPI.policiesAllTypesList(requestInit); }; - protected updated(changedProperties: PropertyValues): void { + protected override updated(changedProperties: PropertyValues): void { super.updated(changedProperties); if (changedProperties.has("showBindingPage")) { @@ -57,25 +70,81 @@ export class PolicyWizard extends CreateWizard { } } - protected createBindingActivate = async (page: FormWizardPage) => { - const createSlot = page.host.steps[1]; - const bindingForm = page.querySelector("ak-policy-binding-form"); + protected createBindingActivate = async ( + page: FormWizardPage<{ "initial": PolicyBindingCheckTarget; "create-binding": Policy }>, + ) => { + const createSlot = page.host.steps[1] as "create-binding"; + const bindingForm = page.querySelector("ak-policy-binding-form"); if (!bindingForm) return; - bindingForm.instance = { - policy: (page.host.state[createSlot] as Policy).pk, - } as PolicyBinding; + if (page.host.state[createSlot]) { + bindingForm.allowedTypes = [PolicyBindingCheckTarget.Policy]; + bindingForm.policyGroupUser = PolicyBindingCheckTarget.Policy; + + const policyBindingRequest: Partial = { + policy: (page.host.state[createSlot] as Policy).pk, + }; + + bindingForm.instance = policyBindingRequest as unknown as PolicyBinding; + } + if (page.host.state[initialStep]) { + bindingForm.allowedTypes = [page.host.state[initialStep]]; + bindingForm.policyGroupUser = page.host.state[initialStep]; + } }; + protected override renderCreateBefore(): SlottedTemplateResult { + if (!this.showBindingPage) { + return null; + } + + return html` + []} + @change=${(ev: CustomEvent>) => { + if (!this.wizard) { + return; + } + + this.wizard.state[initialStep] = ev.detail.value; + this.wizard.navigateNext(); + }} + > + + `; + } + protected renderForms(): SlottedTemplateResult { const bindingPage = this.showBindingPage ? html` - + > ` : null; diff --git a/web/src/admin/providers/ProviderListPage.ts b/web/src/admin/providers/ProviderListPage.ts index 4d48de0393..7f1ae00ff2 100644 --- a/web/src/admin/providers/ProviderListPage.ts +++ b/web/src/admin/providers/ProviderListPage.ts @@ -1,4 +1,3 @@ -import "#admin/applications/ApplicationWizardHint"; import "#admin/providers/ak-provider-wizard"; import "#admin/providers/google_workspace/GoogleWorkspaceProviderForm"; import "#admin/providers/ldap/LDAPProviderForm"; diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts index 2899768402..a32019ecdf 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts @@ -1,5 +1,4 @@ import "#admin/providers/RelatedApplicationButton"; -import "#admin/providers/oauth2/OAuth2ProviderForm"; import "#admin/events/ObjectChangelog"; import "#admin/rbac/ak-rbac-object-permission-page"; import "#admin/rbac/ObjectPermissionModal"; @@ -15,10 +14,13 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { SlottedTemplateResult } from "#elements/types"; import renderDescriptionList from "#components/DescriptionList"; +import { OAuth2ProviderFormPage } from "#admin/providers/oauth2/OAuth2ProviderForm"; + import { ClientTypeEnum, CoreApi, @@ -238,21 +240,14 @@ export class OAuth2ProviderViewPage extends AKElement { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update OAuth2 Provider")} - - - - `, + html``, ], ])} diff --git a/web/src/admin/providers/ssf/SSFProviderViewPage.ts b/web/src/admin/providers/ssf/SSFProviderViewPage.ts index feb1add29d..b7578da5c8 100644 --- a/web/src/admin/providers/ssf/SSFProviderViewPage.ts +++ b/web/src/admin/providers/ssf/SSFProviderViewPage.ts @@ -1,5 +1,4 @@ import "#admin/providers/RelatedApplicationButton"; -import "#admin/providers/ssf/SSFProviderFormPage"; import "#admin/providers/ssf/StreamTable"; import "#admin/events/ObjectChangelog"; import "#admin/rbac/ObjectPermissionModal"; @@ -14,10 +13,13 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { SlottedTemplateResult } from "#elements/types"; import renderDescriptionList from "#components/DescriptionList"; +import { SSFProviderFormPage } from "#admin/providers/ssf/SSFProviderFormPage"; + import { ModelEnum, ProvidersApi, SSFProvider } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -155,15 +157,14 @@ export class SSFProviderViewPage extends AKElement { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update SSF Provider")} - - - - `, + html``, ], ])} diff --git a/web/src/admin/roles/ak-related-role-table.ts b/web/src/admin/roles/ak-related-role-table.ts index f3a1f63dc5..b0b630f00f 100644 --- a/web/src/admin/roles/ak-related-role-table.ts +++ b/web/src/admin/roles/ak-related-role-table.ts @@ -83,7 +83,10 @@ export class AddRelatedRoleForm extends Form<{ roles: string[] }> {
    - + ${this.rolesToAdd.map((role) => { return html`
    - + ${this.permissionsToAdd.map((permission) => { return html` - ${msg("Save Changes")} - ${msg("Update Role")} - - - - `, + html``, ], ])}
    diff --git a/web/src/admin/sources/BaseSourceForm.ts b/web/src/admin/sources/BaseSourceForm.ts index c05b5e492c..638a0b9ce3 100644 --- a/web/src/admin/sources/BaseSourceForm.ts +++ b/web/src/admin/sources/BaseSourceForm.ts @@ -3,6 +3,8 @@ import { ModelForm } from "#elements/forms/ModelForm"; import { msg } from "@lit/localize"; export abstract class BaseSourceForm extends ModelForm { + public static override verboseName = msg("Source"); + public static override verboseNamePlural = msg("Sources"); getSuccessMessage(): string { return this.instance ? msg("Successfully updated source.") diff --git a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts index 3d4f41e68b..c10a3eebf7 100644 --- a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts +++ b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts @@ -1,6 +1,5 @@ import "#admin/rbac/ak-rbac-object-permission-page"; import "#admin/sources/ldap/LDAPSourceConnectivity"; -import "#admin/sources/ldap/LDAPSourceForm"; import "#admin/sources/ldap/LDAPSourceUserList"; import "#admin/sources/ldap/LDAPSourceGroupList"; import "#admin/events/ObjectChangelog"; @@ -8,7 +7,6 @@ import "#elements/CodeMirror"; import "#elements/Tabs"; import "#elements/buttons/ActionButton/index"; import "#elements/buttons/SpinnerButton/index"; -import "#elements/forms/ModalForm"; import "#elements/sync/SyncStatusCard"; import "#elements/tasks/ScheduleList"; @@ -16,10 +14,13 @@ import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { SlottedTemplateResult } from "#elements/types"; import renderDescriptionList from "#components/DescriptionList"; +import { LDAPSourceForm } from "#admin/sources/ldap/LDAPSourceForm"; + import { LDAPSource, ModelEnum, SourcesApi } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -105,23 +106,14 @@ export class LDAPSourceViewPage extends AKElement { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update LDAP Source")} - - - - `, + html``, ], ], { twocolumn: true }, diff --git a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts index 78593a14bf..d71eb23450 100644 --- a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts +++ b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts @@ -1,22 +1,23 @@ import "#admin/policies/BoundPoliciesList"; import "#admin/rbac/ak-rbac-object-permission-page"; import "#admin/sources/oauth/OAuthSourceDiagram"; -import "#admin/sources/oauth/OAuthSourceForm"; import "#admin/events/ObjectChangelog"; import "#elements/CodeMirror"; import "#elements/Tabs"; import "#elements/buttons/SpinnerButton/index"; -import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; import { AKElement } from "#elements/Base"; +import { modalInvoker } from "#elements/dialogs"; import { sourceBindingTypeNotices } from "#elements/sources/utils"; import { SlottedTemplateResult } from "#elements/types"; import renderDescriptionList from "#components/DescriptionList"; +import { OAuthSourceForm } from "#admin/sources/oauth/OAuthSourceForm"; + import { ModelEnum, OAuthSource, ProviderTypeEnum, SourcesApi } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -137,21 +138,14 @@ export class OAuthSourceViewPage extends AKElement { ], [ msg("Related actions"), - html` - ${msg("Save Changes")} - ${msg("Update OAuth Source")} - - - - `, + html``, ], ])}
    diff --git a/web/src/admin/stages/ak-stage-wizard.ts b/web/src/admin/stages/ak-stage-wizard.ts index b67cd79fa1..21e31367ab 100644 --- a/web/src/admin/stages/ak-stage-wizard.ts +++ b/web/src/admin/stages/ak-stage-wizard.ts @@ -3,16 +3,17 @@ import "#elements/LicenseNotice"; import "#elements/wizard/FormWizardPage"; import "#elements/wizard/TypeCreateWizardPage"; import "#elements/wizard/Wizard"; +import "#elements/forms/FormGroup"; +import "#admin/flows/StageBindingForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { RadioOption } from "#elements/forms/Radio"; import { SlottedTemplateResult } from "#elements/types"; import { CreateWizard } from "#elements/wizard/CreateWizard"; import { FormWizardPage } from "#elements/wizard/FormWizardPage"; import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage"; -import { StageBindingForm } from "#admin/flows/StageBindingForm"; - import { FlowStageBinding, Stage, StagesApi, TypeCreate } from "@goauthentik/api"; import { msg } from "@lit/localize"; @@ -27,8 +28,8 @@ export class AKStageWizard extends CreateWizard { @property({ type: Boolean }) public showBindingPage = false; - @property() - public bindingTarget?: string; + @property({ type: String, useDefault: true }) + public bindingTarget: string | null = null; public override initialSteps = this.showBindingPage ? ["initial", "create-binding"] @@ -39,11 +40,14 @@ export class AKStageWizard extends CreateWizard { public override layout = TypeCreateWizardPageLayouts.list; + public override groupLabel = msg("Bind New Stage"); + public override groupDescription = msg("Select the type of stage you want to create."); + protected apiEndpoint = async (requestInit?: RequestInit): Promise => { return this.#api.stagesAllTypesList(requestInit); }; - protected updated(changedProperties: PropertyValues): void { + protected override updated(changedProperties: PropertyValues): void { super.updated(changedProperties); if (changedProperties.has("showBindingPage")) { @@ -51,17 +55,52 @@ export class AKStageWizard extends CreateWizard { } } - protected createBindingActivate = async (context: FormWizardPage) => { - const createSlot = context.host.steps[1]; - const bindingForm = context.querySelector("ak-stage-binding-form"); + protected createBindingActivate = async ( + context: FormWizardPage<{ "create-binding": Stage }>, + ) => { + const createSlot = context.host.steps[1] as "create-binding"; + const bindingForm = context.querySelector("ak-stage-binding-form"); if (!bindingForm) return; - bindingForm.instance = { - stage: (context.host.state[createSlot] as Stage).pk, - } as FlowStageBinding; + if (context.host.state[createSlot]) { + bindingForm.instance = { + stage: (context.host.state[createSlot] as Stage).pk, + } as FlowStageBinding; + } }; + protected override renderCreateBefore(): SlottedTemplateResult { + if (!this.showBindingPage) { + return null; + } + + return html` + []} + @change=${() => { + if (!this.wizard) { + return; + } + + this.wizard.navigateNext(); + }} + > + + `; + } + protected renderForms(): SlottedTemplateResult { const bindingPage = this.showBindingPage ? html`) { + public static override verboseName = msg("User"); + public static override verboseNamePlural = msg("Users"); + + protected coreAPI = new CoreApi(DEFAULT_CONFIG); + + protected override send(): Promise { + if (!this.instance) { + return Promise.reject(new Error("No user instance provided")); + } + const nextActiveState = !this.instance.isActive; + + return this.coreAPI.coreUsersPartialUpdate({ + id: this.instance.pk, + patchedUserRequest: { + isActive: nextActiveState, + }, }); } - onError(error: unknown): Promise { - return parseAPIResponseError(error).then((parsedError) => { - showMessage({ - message: msg( - str`Failed to update ${this.objectLabel}: ${pluckErrorDetail(parsedError)}`, - ), - level: MessageLevel.error, - }); - }); + public override formatSubmitLabel(): string { + return super.formatSubmitLabel( + this.instance?.isActive ? msg("Deactivate") : msg("Activate"), + ); } - override renderModalInner(): TemplateResult { - const objName = this.getFormattedObjectName(); - return html`
    -
    -

    ${msg(str`Update ${this.objectLabel}`)}

    -
    -
    -
    -
    -

    - ${msg(str`Are you sure you want to update ${this.objectLabel}${objName}?`)} -

    -
    -
    - `; + public override formatSubmittingLabel(): string { + return super.formatSubmittingLabel( + this.instance?.isActive ? msg("Deactivating...") : msg("Activating..."), + ); + } + + protected override formatDisplayName(): string { + if (!this.instance) { + return msg("Unknown user"); + } + + return formatDisambiguatedUserDisplayName(this.instance, this.activeLanguageTag); + } + + protected override formatHeadline(): string { + return this.instance?.isActive + ? msg(str`Review ${this.verboseName} Deactivation`, { + id: "form.headline.deactivation", + }) + : msg(str`Review ${this.verboseName} Activation`, { id: "form.headline.activation" }); + } + + public override usedBy = (): Promise => { + if (!this.instance) { + return Promise.resolve([]); + } + + return this.coreAPI.coreUsersUsedByList({ id: this.instance.pk }); + }; + + protected override renderUsedBySection(): SlottedTemplateResult { + if (this.instance?.isActive) { + return super.renderUsedBySection(); + } + + const displayName = this.formatDisplayName(); + const { usedByList, verboseName } = this; + + return html` +
    + ${displayName} +
    + { + return [pluckEntityName(ub) || msg("Unnamed"), html`${ub.pk}`]; + })} + > +
    `; } } declare global { interface HTMLElementTagNameMap { - "ak-user-active-form": UserActiveForm; + "ak-user-activation-toggle-form": UserActivationToggleForm; } } + +export interface ToggleUserActivationButtonProps { + className?: string; +} + +export function ToggleUserActivationButton( + user: User, + { className = "" }: ToggleUserActivationButtonProps = {}, +): SlottedTemplateResult { + const label = user.isActive ? msg("Deactivate") : msg("Activate"); + const tooltip = user.isActive + ? msg("Lock the user out of this system") + : msg("Allow the user to log in and use this system"); + + return html``; +} diff --git a/web/src/admin/users/UserListPage.ts b/web/src/admin/users/UserListPage.ts index 213a783c3a..612914e444 100644 --- a/web/src/admin/users/UserListPage.ts +++ b/web/src/admin/users/UserListPage.ts @@ -30,6 +30,7 @@ import { SlottedTemplateResult } from "#elements/types"; import { AKUserWizard } from "#admin/users/ak-user-wizard"; import { RecoveryButtons } from "#admin/users/recovery"; +import { ToggleUserActivationButton } from "#admin/users/UserActiveForm"; import { UserForm } from "#admin/users/UserForm"; import { UserImpersonateForm } from "#admin/users/UserImpersonateForm"; @@ -69,7 +70,7 @@ export class UserListPage extends WithBrandConfig( .pf-c-avatar { max-height: var(--pf-c-avatar--Height); max-width: var(--pf-c-avatar--Width); - margin-bottom: calc(var(--pf-c-avatar--Width) * -0.6); + vertical-align: middle; } `, ]; @@ -251,10 +252,19 @@ export class UserListPage extends WithBrandConfig( const displayName = formatUserDisplayName(item); return [ - html``, - html` -
    ${item.username}
    - ${item.name ? item.name : html`<${msg("No name set")}>`} + html`${msg(str`Avatar`, + html`
    +
    ${item.username}
    + ${displayName ? item.name : html`<${msg("No name set")}>`}
    `, html``, Timestamp(item.lastLogin), @@ -300,22 +310,7 @@ export class UserListPage extends WithBrandConfig(
    - { - return this.#api.coreUsersPartialUpdate({ - id: item.pk, - patchedUserRequest: { - isActive: !item.isActive, - }, - }); - }} - > - - + ${ToggleUserActivationButton(item)}
    diff --git a/web/src/admin/users/UserViewPage.ts b/web/src/admin/users/UserViewPage.ts index ead0b48935..7d81a724e5 100644 --- a/web/src/admin/users/UserViewPage.ts +++ b/web/src/admin/users/UserViewPage.ts @@ -28,27 +28,34 @@ import "./UserDevicesTable.js"; import "#elements/ak-mdx/ak-mdx"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { AKRefreshEvent } from "#common/events"; import { userTypeToLabel } from "#common/labels"; -import { formatUserDisplayName } from "#common/users"; +import { formatDisambiguatedUserDisplayName, formatUserDisplayName } from "#common/users"; import { AKElement } from "#elements/Base"; +import { listen } from "#elements/decorators/listen"; +import { showAPIErrorMessage } from "#elements/messages/MessageContainer"; import { WithBrandConfig } from "#elements/mixins/branding"; import { WithCapabilitiesConfig } from "#elements/mixins/capabilities"; +import { WithLicenseSummary } from "#elements/mixins/license"; +import { WithLocale } from "#elements/mixins/locale"; import { WithSession } from "#elements/mixins/session"; import { Timestamp } from "#elements/table/shared"; +import { SlottedTemplateResult } from "#elements/types"; import { setPageDetails } from "#components/ak-page-navbar"; import { type DescriptionPair, renderDescriptionList } from "#components/DescriptionList"; import { RecoveryButtons } from "#admin/users/recovery"; +import { ToggleUserActivationButton } from "#admin/users/UserActiveForm"; import { UserForm } from "#admin/users/UserForm"; import { UserImpersonateForm } from "#admin/users/UserImpersonateForm"; import { CapabilitiesEnum, CoreApi, ModelEnum, User } from "@goauthentik/api"; import { msg, str } from "@lit/localize"; -import { css, html, nothing, PropertyValues, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators.js"; +import { css, html, PropertyValues, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; @@ -62,20 +69,16 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; @customElement("ak-user-view") -export class UserViewPage extends WithBrandConfig(WithCapabilitiesConfig(WithSession(AKElement))) { - @property({ type: Number }) - set userId(id: number) { - new CoreApi(DEFAULT_CONFIG) - .coreUsersRetrieve({ - id: id, - }) - .then((user) => { - this.user = user; - }); - } +export class UserViewPage extends WithLicenseSummary( + WithLocale(WithBrandConfig(WithCapabilitiesConfig(WithSession(AKElement)))), +) { + #api = new CoreApi(DEFAULT_CONFIG); - @state() - protected user: User | null = null; + @property({ type: Number, useDefault: true }) + public userId: number | null = null; + + @property({ attribute: false, useDefault: true }) + public user: User | null = null; static styles = [ PFPage, @@ -103,26 +106,64 @@ export class UserViewPage extends WithBrandConfig(WithCapabilitiesConfig(WithSes `, ]; - renderUserCard() { + @listen(AKRefreshEvent) + public refresh = () => { + if (!this.userId) { + return; + } + + return this.#api + .coreUsersRetrieve({ + id: this.userId!, + }) + .then((user) => { + this.user = user; + }) + .catch(showAPIErrorMessage); + }; + + protected override updated(changed: PropertyValues) { + super.updated(changed); + + if (changed.has("userId") && this.userId !== null) { + this.refresh(); + } + + if (changed.has("user") && this.user) { + const { username, avatar, name, email } = this.user; + const icon = avatar ?? "pf-icon pf-icon-user"; + + setPageDetails({ + icon, + iconImage: !!avatar, + header: username ? msg(str`User ${username}`) : msg("User"), + description: this.user + ? formatDisambiguatedUserDisplayName({ name, email }, this.activeLanguageTag) + : null, + }); + } + } + + protected renderUserCard() { if (!this.user) { - return nothing; + return null; } const user = this.user; // prettier-ignore const userInfo: DescriptionPair[] = [ - [msg("Username"), user.username], - [msg("Name"), user.name], - [msg("Email"), user.email || "-"], - [msg("Last login"), Timestamp(user.lastLogin)], - [msg("Last password change"), Timestamp(user.passwordChangeDate)], - [msg("Active"), html``], - [msg("Type"), userTypeToLabel(user.type)], - [msg("Superuser"), html``], - [msg("Actions"), this.renderActionButtons(user)], - [msg("Recovery"), this.renderRecoveryButtons(user)], - ]; + [ msg("Username"), user.username ], + [ msg("Name"), user.name ], + [ msg("Email"), user.email || "-" ], + [ msg("Last login"), Timestamp(user.lastLogin) ], + [ msg("Last password change"), Timestamp(user.passwordChangeDate) ], + [ msg("Active"), html`` ], + [ msg("Type"), userTypeToLabel(user.type) ], + [ msg("Superuser"), html`` ], + [ msg("Actions"), this.renderActionButtons(user) ], + [ msg("Recovery"), this.renderRecoveryButtons(user) ], + ] return html`
    ${msg("User Info")}
    @@ -132,7 +173,7 @@ export class UserViewPage extends WithBrandConfig(WithCapabilitiesConfig(WithSes `; } - renderActionButtons(user: User) { + protected renderActionButtons(user: User): SlottedTemplateResult { const showImpersonate = this.can(CapabilitiesEnum.CanImpersonate) && user.pk !== this.currentUser?.pk; @@ -145,29 +186,8 @@ export class UserViewPage extends WithBrandConfig(WithCapabilitiesConfig(WithSes > ${msg("Edit User")} - { - return new CoreApi(DEFAULT_CONFIG).coreUsersPartialUpdate({ - id: user.pk, - patchedUserRequest: { - isActive: !user.isActive, - }, - }); - }} - > - - + + ${ToggleUserActivationButton(user, { className: "pf-m-block" })} ${showImpersonate ? html`