Merge branch 'master' into 20250226-rfc2307-ldap

This commit is contained in:
Firstyear 2025-03-14 10:34:41 +10:00 committed by GitHub
commit b6d4108a62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 547 additions and 327 deletions

View file

@ -19,9 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: Install dependencies
run: |
sudo apt-get update && \
@ -41,8 +39,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: "Run cargo fmt"
run: cargo fmt --check

View file

@ -24,9 +24,7 @@ jobs:
with:
ref: ${{ inputs.tag }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: Install deps
run: |
sudo apt-get update

View file

@ -27,10 +27,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: Install dependencies
run: |
sudo apt-get update && \
@ -75,10 +72,7 @@ jobs:
with:
toolchain: ${{ matrix.rust_version }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: Install dependencies
run: |
sudo apt-get update && \
@ -118,10 +112,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- name: Install dependencies
run: |
sudo apt-get update && \

View file

@ -28,9 +28,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.7
with:
version: "v0.4.2"
uses: mozilla-actions/sccache-action@v0.0.8
- run: cargo build --locked -p kanidm_client -p kanidm_tools --bin kanidm
# yamllint disable-line rule:line-length
- run: cargo test -p kanidm_client -p kanidm_tools

7
Cargo.lock generated
View file

@ -3254,6 +3254,7 @@ dependencies = [
"kanidm_proto",
"kanidmd_core",
"kanidmd_lib",
"ldap3_client",
"oauth2 4.4.2",
"openssl",
"petgraph",
@ -3364,7 +3365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.48.5",
]
[[package]]
@ -4742,9 +4743,9 @@ dependencies = [
[[package]]
name = "ring"
version = "0.17.10"
version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34b5020fcdea098ef7d95e9f89ec15952123a4a039badd09fabebe9e963e839"
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [
"cc",
"cfg-if",

View file

@ -58,6 +58,21 @@ can only use the UID range `1879048192` (`0x70000000`) to `2147483647` (`0x7ffff
limitations of the Linux kernel and
[systemd reserving other uids in the range](http://systemd.io/UIDS-GIDS/) for its exclusive use.
| name | min | max |
|------|-----|-----|
| system | 0 | 999 |
| user | 1000 | 60000 |
| systemd homed | 60001 | 60577 |
| unused A | 60578 | 61183 |
| systemd dynuser | 61184 | 65519 |
| unused B | 65520 | 65533 |
| nobody | 65534 | 65534 |
| 16bit limit | 65535 | 65535 |
| unused C | 65536 | 524287 |
| systemd nspawn | 524288 | 1879048191 |
| kanidm dyn alloc | 1879048192 | 2147483647 |
| unusable | 2147483648 | 4294967295 |
A valid concern is the possibility of duplication in the lower 24 bits. Given the
[birthday problem](https://en.wikipedia.org/wiki/Birthday_problem), if you have ~7700 groups and
accounts, you have a 50% chance of duplication. With ~5000 you have a 25% chance, ~930 you have a 1%
@ -67,7 +82,7 @@ We advise that if you have a site with greater than approximately 2,000 users yo
external system to allocate GID numbers serially or consistently to avoid potential duplication
events.
We recommend the use of the range `65536` through `524287` for manual allocation. This leaves the
We recommend the use of the range `65536` through `524287` (`unused C`) for manual allocation. This leaves the
range `1000` through `65535` for OS/Distro purposes, and allows Kanidm to continue dynamic
allocation in the range `1879048192` to `2147483647` if you choose a hybrid allocation approach.

View file

@ -54,7 +54,6 @@ services:
- traefik.http.routers.kanidm.entrypoints=websecure
- traefik.http.routers.kanidm.rule=Host(`idm.example.com`)
- traefik.http.routers.kanidm.service=kanidm
- traefik.http.serversTransports.kanidm.insecureSkipVerify=true
- traefik.http.services.kanidm.loadbalancer.server.port=8443
- traefik.http.services.kanidm.loadbalancer.server.scheme=https
volumes:

View file

@ -7,7 +7,8 @@ use serde::{Deserialize, Serialize};
use serde_with::base64::{Base64, UrlSafe};
use serde_with::formats::SpaceSeparator;
use serde_with::{
formats, serde_as, skip_serializing_none, NoneAsEmptyString, StringWithSeparator,
formats, rust::deserialize_ignore_any, serde_as, skip_serializing_none, NoneAsEmptyString,
StringWithSeparator,
};
use url::Url;
use uuid::Uuid;
@ -353,6 +354,9 @@ pub enum ResponseType {
pub enum ResponseMode {
Query,
Fragment,
FormPost,
#[serde(other, deserialize_with = "deserialize_ignore_any")]
Invalid,
}
fn response_modes_supported_default() -> Vec<ResponseMode> {

190
pykanidm/poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@ -6,6 +6,7 @@ version = "2.4.0"
description = "Happy Eyeballs for asyncio"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"},
{file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"},
@ -17,6 +18,7 @@ version = "3.11.13"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
{file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4fe27dbbeec445e6e1291e61d61eb212ee9fed6e47998b27de71d70d3e8777d"},
{file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e64ca2dbea28807f8484c13f684a2f761e69ba2640ec49dacd342763cc265ef"},
@ -112,7 +114,7 @@ propcache = ">=0.2.0"
yarl = ">=1.17.0,<2.0"
[package.extras]
speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
[[package]]
name = "aiosignal"
@ -120,6 +122,7 @@ version = "1.3.1"
description = "aiosignal: a list of registered asynchronous callbacks"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
files = [
{file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
{file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
@ -134,6 +137,7 @@ version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@ -145,6 +149,7 @@ version = "3.2.4"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"},
{file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"},
@ -159,6 +164,8 @@ version = "4.0.3"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
markers = "python_version < \"3.11\""
files = [
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
@ -170,18 +177,19 @@ version = "24.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
files = [
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
]
[package.extras]
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"]
cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"]
dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"]
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""]
[[package]]
name = "authlib"
@ -189,6 +197,7 @@ version = "1.5.1"
description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "authlib-1.5.1-py2.py3-none-any.whl", hash = "sha256:8408861cbd9b4ea2ff759b00b6f02fd7d81ac5a56d0b2b22c08606c6049aae11"},
{file = "authlib-1.5.1.tar.gz", hash = "sha256:5cbc85ecb0667312c1cdc2f9095680bb735883b123fb509fde1e65b1c5df972e"},
@ -203,6 +212,7 @@ version = "2.16.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"},
{file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
@ -217,6 +227,7 @@ version = "5.8"
description = "A wrapper around re and regex that adds additional back references."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"},
{file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"},
@ -235,6 +246,7 @@ version = "2024.8.30"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
{file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
@ -246,6 +258,8 @@ version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@ -325,6 +339,7 @@ version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
groups = ["dev"]
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
@ -424,6 +439,7 @@ version = "8.1.7"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
@ -438,6 +454,7 @@ version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@ -449,6 +466,7 @@ version = "7.6.12"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
{file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
@ -516,7 +534,7 @@ files = [
]
[package.extras]
toml = ["tomli"]
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
[[package]]
name = "cryptography"
@ -524,6 +542,7 @@ version = "43.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
@ -573,6 +592,7 @@ version = "0.3.8"
description = "serialize all of Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
{file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
@ -588,6 +608,8 @@ version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
@ -602,6 +624,7 @@ version = "1.4.1"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
@ -688,6 +711,7 @@ version = "2.1.3"
description = "URL manipulation made simple."
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "furl-2.1.3-py2.py3-none-any.whl", hash = "sha256:9ab425062c4217f9802508e45feb4a83e54324273ac4b202f1850363309666c0"},
{file = "furl-2.1.3.tar.gz", hash = "sha256:5a6188fe2666c484a12159c18be97a1977a71d632ef5bb867ef15f54af39cc4e"},
@ -703,6 +727,7 @@ version = "2.1.0"
description = "Copy your docs directly to the gh-pages branch."
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
@ -720,6 +745,7 @@ version = "1.2.0"
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "griffe-1.2.0-py3-none-any.whl", hash = "sha256:a8b2fcb1ecdc5a412e646b0b4375eb20a5d2eac3a11dd8c10c56967a4097663c"},
{file = "griffe-1.2.0.tar.gz", hash = "sha256:1c9f6ef7455930f3f9b0c4145a961c90385d1e2cbc496f7796fbff560ec60d31"},
@ -734,6 +760,7 @@ version = "3.8"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.6"
groups = ["main", "dev"]
files = [
{file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"},
{file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"},
@ -745,6 +772,8 @@ version = "8.4.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
markers = "python_version < \"3.10\""
files = [
{file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"},
{file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"},
@ -756,7 +785,7 @@ zipp = ">=0.5"
[package.extras]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
[[package]]
name = "iniconfig"
@ -764,6 +793,7 @@ version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@ -775,6 +805,7 @@ version = "5.13.2"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
@ -789,6 +820,7 @@ version = "3.1.5"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
{file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
@ -806,6 +838,7 @@ version = "4.23.0"
description = "An implementation of JSON Schema validation for Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"},
{file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"},
@ -827,6 +860,7 @@ version = "2023.12.1"
description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"},
{file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"},
@ -841,6 +875,7 @@ version = "3.7"
description = "Python implementation of John Gruber's Markdown."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"},
{file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"},
@ -859,6 +894,7 @@ version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
@ -928,6 +964,7 @@ version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
@ -939,6 +976,7 @@ version = "1.3.4"
description = "A deep merge function for 🐍."
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
{file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
@ -950,6 +988,7 @@ version = "1.6.1"
description = "Project documentation with Markdown."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"},
{file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"},
@ -973,7 +1012,7 @@ watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
[[package]]
name = "mkdocs-autorefs"
@ -981,6 +1020,7 @@ version = "1.4.0"
description = "Automatically link across pages in MkDocs."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "mkdocs_autorefs-1.4.0-py3-none-any.whl", hash = "sha256:bad19f69655878d20194acd0162e29a89c3f7e6365ffe54e72aa3fd1072f240d"},
{file = "mkdocs_autorefs-1.4.0.tar.gz", hash = "sha256:a9c0aa9c90edbce302c09d050a3c4cb7c76f8b7b2c98f84a7a05f53d00392156"},
@ -997,6 +1037,7 @@ version = "0.2.0"
description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
{file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
@ -1010,13 +1051,14 @@ pyyaml = ">=5.1"
[[package]]
name = "mkdocs-material"
version = "9.6.6"
version = "9.6.7"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "mkdocs_material-9.6.6-py3-none-any.whl", hash = "sha256:904c422ec86086144495831cee2614bb8a0092572ef579af6392b8080309d3a3"},
{file = "mkdocs_material-9.6.6.tar.gz", hash = "sha256:06141bd720b0b235829bd59e8afc11d5587c35ae7fc340612d2b3f554e6a69d8"},
{file = "mkdocs_material-9.6.7-py3-none-any.whl", hash = "sha256:8a159e45e80fcaadd9fbeef62cbf928569b93df954d4dc5ba76d46820caf7b47"},
{file = "mkdocs_material-9.6.7.tar.gz", hash = "sha256:3e2c1fceb9410056c2d91f334a00cdea3215c28750e00c691c1e46b2a33309b4"},
]
[package.dependencies]
@ -1043,6 +1085,7 @@ version = "1.3.1"
description = "Extension pack for Python Markdown and MkDocs Material."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"},
{file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"},
@ -1050,13 +1093,14 @@ files = [
[[package]]
name = "mkdocstrings"
version = "0.28.2"
version = "0.28.3"
description = "Automatic documentation from sources, for MkDocs."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "mkdocstrings-0.28.2-py3-none-any.whl", hash = "sha256:57f79c557e2718d217d6f6a81bf75a0de097f10e922e7e5e00f085c3f0ff6895"},
{file = "mkdocstrings-0.28.2.tar.gz", hash = "sha256:9b847266d7a588ea76a8385eaebe1538278b4361c0d1ce48ed005be59f053569"},
{file = "mkdocstrings-0.28.3-py3-none-any.whl", hash = "sha256:df5351ffd10477aa3c2ff5cdf17544b936477195436923660274d084a5c1359c"},
{file = "mkdocstrings-0.28.3.tar.gz", hash = "sha256:c753516b1b6cee12d00bf9c28255e22c0d71f34c721ca668971fce885d846e0f"},
]
[package.dependencies]
@ -1072,24 +1116,25 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""}
[package.extras]
crystal = ["mkdocstrings-crystal (>=0.3.4)"]
python = ["mkdocstrings-python (>=0.5.2)"]
python = ["mkdocstrings-python (>=1.16.2)"]
python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
[[package]]
name = "mkdocstrings-python"
version = "1.16.2"
version = "1.16.3"
description = "A Python handler for mkdocstrings."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "mkdocstrings_python-1.16.2-py3-none-any.whl", hash = "sha256:ff7e719404e59ad1a72f1afbe854769984c889b8fa043c160f6c988e1ad9e966"},
{file = "mkdocstrings_python-1.16.2.tar.gz", hash = "sha256:942ec1a2e0481d28f96f93be3d6e343cab92a21e5baf01c37dd2d7236c4d0bd7"},
{file = "mkdocstrings_python-1.16.3-py3-none-any.whl", hash = "sha256:17b4d7a1add16032dff4b2f54b51d889fbd5d2fa87c166b22fbc3a56d37ec75e"},
{file = "mkdocstrings_python-1.16.3.tar.gz", hash = "sha256:d0cdee60399b397e7cbb25075cac4aa7a27b34f75621eb1898dfebc98b88d600"},
]
[package.dependencies]
griffe = ">=0.49"
mkdocs-autorefs = ">=1.4"
mkdocstrings = ">=0.28.2"
mkdocstrings = ">=0.28.3"
typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
[[package]]
@ -1098,6 +1143,7 @@ version = "6.0.5"
description = "multidict implementation"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
files = [
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
@ -1197,6 +1243,7 @@ version = "1.15.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
{file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
@ -1250,6 +1297,7 @@ version = "1.0.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
python-versions = ">=3.5"
groups = ["dev"]
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
@ -1261,6 +1309,7 @@ version = "1.0.1"
description = "Ordered Multivalue Dictionary"
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "orderedmultidict-1.0.1-py2.py3-none-any.whl", hash = "sha256:43c839a17ee3cdd62234c47deca1a8508a3f2ca1d0678a3bf791c87cf84adbf3"},
{file = "orderedmultidict-1.0.1.tar.gz", hash = "sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad"},
@ -1275,6 +1324,7 @@ version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
@ -1286,6 +1336,7 @@ version = "0.5.7"
description = "Divides large result sets into pages for easier browsing"
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"},
{file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"},
@ -1301,6 +1352,7 @@ version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@ -1312,6 +1364,7 @@ version = "4.3.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"},
{file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"},
@ -1328,6 +1381,7 @@ version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@ -1343,6 +1397,7 @@ version = "2.1.3"
description = "HTTP traffic mocking and expectations made easy"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pook-2.1.3-py3-none-any.whl", hash = "sha256:f8e75e2e41b1f6da37d0bc6b77a0f4da33c4d4de382105046efd644fe5ca2f8e"},
{file = "pook-2.1.3.tar.gz", hash = "sha256:441191c0f3d014b141ca71430a0c2bfa6d2369ac24703a3fdfbbf5a25146d8c0"},
@ -1359,6 +1414,7 @@ version = "0.2.0"
description = "Accelerated property cache"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"},
{file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"},
@ -1466,6 +1522,8 @@ version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@ -1477,6 +1535,7 @@ version = "2.10.6"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
{file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
@ -1489,7 +1548,7 @@ typing-extensions = ">=4.12.2"
[package.extras]
email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata"]
timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
[[package]]
name = "pydantic-core"
@ -1497,6 +1556,7 @@ version = "2.27.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
{file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
@ -1609,6 +1669,7 @@ version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
@ -1623,6 +1684,7 @@ version = "3.2.7"
description = "python code static checker"
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"},
{file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"},
@ -1634,7 +1696,7 @@ colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""},
{version = ">=0.3.7", markers = "python_version >= \"3.12\""},
{version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
{version = ">=0.3.6", markers = "python_version == \"3.11\""},
]
isort = ">=4.2.5,<5.13.0 || >5.13.0,<6"
mccabe = ">=0.6,<0.8"
@ -1653,6 +1715,7 @@ version = "0.8.2"
description = "Utilities and helpers for writing Pylint plugins"
optional = false
python-versions = ">=3.7,<4.0"
groups = ["dev"]
files = [
{file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"},
{file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"},
@ -1667,6 +1730,7 @@ version = "0.3.5"
description = "A Pylint plugin to help Pylint understand the Pydantic"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pylint_pydantic-0.3.5-py3-none-any.whl", hash = "sha256:e7a54f09843b000676633ed02d5985a4a61c8da2560a3b0d46082d2ff171c4a1"},
]
@ -1682,6 +1746,7 @@ version = "10.9"
description = "Extension pack for Python Markdown."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"},
{file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"},
@ -1700,6 +1765,7 @@ version = "8.3.5"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"},
{file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"},
@ -1722,6 +1788,7 @@ version = "1.1.0"
description = "Pytest plugin for aiohttp support"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pytest_aiohttp-1.1.0-py3-none-any.whl", hash = "sha256:f39a11693a0dce08dd6c542d241e199dd8047a6e6596b2bcfa60d373f143456d"},
{file = "pytest_aiohttp-1.1.0.tar.gz", hash = "sha256:147de8cb164f3fc9d7196967f109ab3c0b93ea3463ab50631e56438eab7b5adc"},
@ -1741,6 +1808,7 @@ version = "0.25.3"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"},
{file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"},
@ -1759,6 +1827,7 @@ version = "3.14.0"
description = "Thin-wrapper around the mock package for easier use with pytest"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"},
{file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"},
@ -1776,6 +1845,7 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["dev"]
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@ -1790,6 +1860,7 @@ version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@ -1852,6 +1923,7 @@ version = "0.1"
description = "A custom YAML tag for referencing environment variables in YAML files. "
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
@ -1866,6 +1938,7 @@ version = "0.35.1"
description = "JSON Referencing + Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"},
{file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"},
@ -1881,6 +1954,7 @@ version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
@ -1902,6 +1976,7 @@ version = "0.20.0"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"},
{file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"},
@ -2010,29 +2085,30 @@ files = [
[[package]]
name = "ruff"
version = "0.9.9"
version = "0.9.10"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"},
{file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"},
{file = "ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb"},
{file = "ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0"},
{file = "ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17"},
{file = "ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1"},
{file = "ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57"},
{file = "ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e"},
{file = "ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1"},
{file = "ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1"},
{file = "ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf"},
{file = "ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933"},
{file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"},
{file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"},
{file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"},
{file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"},
{file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"},
{file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"},
{file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"},
{file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"},
{file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"},
{file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"},
{file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"},
{file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"},
]
[[package]]
@ -2041,6 +2117,7 @@ version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
groups = ["dev"]
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@ -2052,6 +2129,7 @@ version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
groups = ["main"]
files = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@ -2063,6 +2141,8 @@ version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version < \"3.11\""
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
@ -2074,6 +2154,7 @@ version = "0.13.2"
description = "Style preserving TOML library"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
{file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
@ -2081,13 +2162,14 @@ files = [
[[package]]
name = "types-requests"
version = "2.32.0.20250301"
version = "2.32.0.20250306"
description = "Typing stubs for requests"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "types_requests-2.32.0.20250301-py3-none-any.whl", hash = "sha256:0003e0124e2cbefefb88222ff822b48616af40c74df83350f599a650c8de483b"},
{file = "types_requests-2.32.0.20250301.tar.gz", hash = "sha256:3d909dc4eaab159c0d964ebe8bfa326a7afb4578d8706408d417e17d61b0c500"},
{file = "types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b"},
{file = "types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1"},
]
[package.dependencies]
@ -2099,6 +2181,7 @@ version = "0.10.8.20240310"
description = "Typing stubs for toml"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"},
{file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"},
@ -2110,6 +2193,7 @@ version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@ -2121,13 +2205,14 @@ version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
@ -2138,6 +2223,7 @@ version = "5.0.2"
description = "Filesystem events monitoring"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877"},
{file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5"},
@ -2180,6 +2266,7 @@ version = "0.13.0"
description = "Makes working with XML feel like you are working with JSON"
optional = false
python-versions = ">=3.4"
groups = ["dev"]
files = [
{file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
{file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
@ -2191,6 +2278,7 @@ version = "1.18.0"
description = "Yet another URL library"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
{file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"},
{file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"},
@ -2287,20 +2375,22 @@ version = "3.20.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
markers = "python_version < \"3.10\""
files = [
{file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"},
{file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
lock-version = "2.1"
python-versions = "^3.9"
content-hash = "5e5caa373faaad974b6688bbbce04f8d9d15f55b2b7f3b184ac456453742e2e3"
content-hash = "3488ad98050ada51068e2874e25b8a5d740c4b20e77829bbac7d2ca3863de055"

View file

@ -29,7 +29,7 @@ Authlib = "^1.2.0"
[tool.poetry.group.dev.dependencies]
ruff = ">=0.5.1,<0.9.10"
ruff = ">=0.5.1,<0.9.11"
pytest = "^8.3.4"
mypy = "^1.14.1"
types-requests = "^2.32.0.20241016"

View file

@ -1,8 +1,5 @@
use std::net;
use std::pin::Pin;
use std::str::FromStr;
use crate::actors::QueryServerReadV1;
use crate::CoreAction;
use futures_util::sink::SinkExt;
use futures_util::stream::StreamExt;
use kanidmd_lib::idm::ldap::{LdapBoundToken, LdapResponseState};
@ -10,13 +7,15 @@ use kanidmd_lib::prelude::*;
use ldap3_proto::proto::LdapMsg;
use ldap3_proto::LdapCodec;
use openssl::ssl::{Ssl, SslAcceptor};
use std::net;
use std::pin::Pin;
use std::str::FromStr;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::{TcpListener, TcpStream};
use tokio_openssl::SslStream;
use tokio_util::codec::{FramedRead, FramedWrite};
use crate::CoreAction;
use tokio::sync::broadcast;
use tokio::sync::mpsc;
use tokio_openssl::SslStream;
use tokio_util::codec::{FramedRead, FramedWrite};
struct LdapSession {
uat: Option<LdapBoundToken>,
@ -49,28 +48,14 @@ async fn client_process_msg(
.await
}
async fn client_process(
tcpstream: TcpStream,
tls_acceptor: SslAcceptor,
async fn client_process<STREAM>(
stream: STREAM,
client_address: net::SocketAddr,
qe_r_ref: &'static QueryServerReadV1,
) {
// Start the event
// From the parameters we need to create an SslContext.
let mut tlsstream = match Ssl::new(tls_acceptor.context())
.and_then(|tls_obj| SslStream::new(tls_obj, tcpstream))
{
Ok(ta) => ta,
Err(e) => {
error!("LDAP TLS setup error, continuing -> {:?}", e);
return;
}
};
if let Err(e) = SslStream::accept(Pin::new(&mut tlsstream)).await {
error!("LDAP TLS accept error, continuing -> {:?}", e);
return;
};
let (r, w) = tokio::io::split(tlsstream);
) where
STREAM: AsyncRead + AsyncWrite,
{
let (r, w) = tokio::io::split(stream);
let mut r = FramedRead::new(r, LdapCodec::default());
let mut w = FramedWrite::new(w, LdapCodec::default());
@ -126,7 +111,32 @@ async fn client_process(
}
}
/// TLS LDAP Listener, hands off to [client_process]
async fn client_tls_accept(
tcpstream: TcpStream,
tls_acceptor: SslAcceptor,
client_socket_addr: net::SocketAddr,
qe_r_ref: &'static QueryServerReadV1,
) {
// Start the event
// From the parameters we need to create an SslContext.
let mut tlsstream = match Ssl::new(tls_acceptor.context())
.and_then(|tls_obj| SslStream::new(tls_obj, tcpstream))
{
Ok(ta) => ta,
Err(err) => {
error!(?err, %client_socket_addr, "LDAP TLS setup error");
return;
}
};
if let Err(err) = SslStream::accept(Pin::new(&mut tlsstream)).await {
error!(?err, %client_socket_addr, "LDAP TLS accept error");
return;
};
tokio::spawn(client_process(tlsstream, client_socket_addr, qe_r_ref));
}
/// TLS LDAP Listener, hands off to [client_tls_accept]
async fn ldap_tls_acceptor(
listener: TcpListener,
mut tls_acceptor: SslAcceptor,
@ -145,10 +155,10 @@ async fn ldap_tls_acceptor(
match accept_result {
Ok((tcpstream, client_socket_addr)) => {
let clone_tls_acceptor = tls_acceptor.clone();
tokio::spawn(client_process(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref));
tokio::spawn(client_tls_accept(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref));
}
Err(e) => {
error!("LDAP acceptor error, continuing -> {:?}", e);
Err(err) => {
warn!(?err, "LDAP acceptor error, continuing");
}
}
}
@ -161,6 +171,34 @@ async fn ldap_tls_acceptor(
info!("Stopped {}", super::TaskName::LdapActor);
}
/// PLAIN LDAP Listener, hands off to [client_process]
async fn ldap_plaintext_acceptor(
listener: TcpListener,
qe_r_ref: &'static QueryServerReadV1,
mut rx: broadcast::Receiver<CoreAction>,
) {
loop {
tokio::select! {
Ok(action) = rx.recv() => {
match action {
CoreAction::Shutdown => break,
}
}
accept_result = listener.accept() => {
match accept_result {
Ok((tcpstream, client_socket_addr)) => {
tokio::spawn(client_process(tcpstream, client_socket_addr, qe_r_ref));
}
Err(e) => {
error!("LDAP acceptor error, continuing -> {:?}", e);
}
}
}
}
}
info!("Stopped {}", super::TaskName::LdapActor);
}
pub(crate) async fn create_ldap_server(
address: &str,
opt_ssl_acceptor: Option<SslAcceptor>,
@ -197,10 +235,7 @@ pub(crate) async fn create_ldap_server(
tls_acceptor_reload_rx,
))
}
None => {
error!("The server won't run without TLS!");
return Err(());
}
None => tokio::spawn(ldap_plaintext_acceptor(listener, qe_r_ref, rx)),
};
info!("Created LDAP interface");

View file

@ -1080,20 +1080,15 @@ pub async fn create_server_core(
Some(la) => {
let opt_ldap_ssl_acceptor = maybe_tls_acceptor.clone();
if !config_test {
// ⚠️ only start the sockets and listeners in non-config-test modes.
let h = ldaps::create_ldap_server(
la.as_str(),
opt_ldap_ssl_acceptor,
server_read_ref,
broadcast_tx.subscribe(),
ldap_tls_acceptor_reload_rx,
)
.await?;
Some(h)
} else {
None
}
let h = ldaps::create_ldap_server(
la.as_str(),
opt_ldap_ssl_acceptor,
server_read_ref,
broadcast_tx.subscribe(),
ldap_tls_acceptor_reload_rx,
)
.await?;
Some(h)
}
None => {
debug!("LDAP not requested, skipping");

View file

@ -34,7 +34,7 @@ fn parse_attributes(
});
if !args_are_allowed {
let msg = "Invalid test config attribute. The following are allow";
let msg = "Invalid test config attribute. The following are allowed";
return Err(syn::Error::new_spanned(
input.sig.fn_token,
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),

View file

@ -205,7 +205,7 @@ pub(crate) trait IdlSqliteTransaction {
let mut stmt = self
.get_conn()?
.prepare(&format!(
"SELECT rowid from {}.sqlite_master where name = :tname LIMIT 1",
"SELECT rowid from {}.sqlite_master where type=\"table\" AND name = :tname LIMIT 1",
self.get_db_name()
))
.map_err(sqlite_error)?;

View file

@ -122,6 +122,11 @@ impl std::fmt::Display for Oauth2Error {
}
// == internal state formats that we encrypt and send.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum SupportedResponseMode {
Query,
Fragment,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
@ -145,7 +150,7 @@ struct ConsentToken {
// We stash some details here for oidc.
pub nonce: Option<String>,
/// The format the response should be returned to the application in.
pub response_mode: ResponseMode,
pub response_mode: SupportedResponseMode,
}
#[serde_as]
@ -239,7 +244,7 @@ pub struct AuthorisePermitSuccess {
// The exchange code as a String
pub code: String,
/// The format the response should be returned to the application in.
pub response_mode: ResponseMode,
response_mode: SupportedResponseMode,
}
impl AuthorisePermitSuccess {
@ -252,7 +257,7 @@ impl AuthorisePermitSuccess {
redirect_uri.set_fragment(None);
match self.response_mode {
ResponseMode::Query => {
SupportedResponseMode::Query => {
redirect_uri
.query_pairs_mut()
.append_pair("code", &self.code);
@ -261,7 +266,7 @@ impl AuthorisePermitSuccess {
redirect_uri.query_pairs_mut().append_pair("state", state);
};
}
ResponseMode::Fragment => {
SupportedResponseMode::Fragment => {
redirect_uri.set_query(None);
// Per [the RFC](https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2), we can't set query pairs on fragment-containing redirects, only query ones.
@ -285,7 +290,7 @@ pub struct AuthoriseReject {
// Where the client wants us to go back to.
pub redirect_uri: Url,
/// The format the response should be returned to the application in.
pub response_mode: ResponseMode,
response_mode: SupportedResponseMode,
}
impl AuthoriseReject {
@ -305,8 +310,8 @@ impl AuthoriseReject {
.finish();
match self.response_mode {
ResponseMode::Query => redirect_uri.set_query(Some(&encoded)),
ResponseMode::Fragment => redirect_uri.set_fragment(Some(&encoded)),
SupportedResponseMode::Query => redirect_uri.set_query(Some(&encoded)),
SupportedResponseMode::Fragment => redirect_uri.set_fragment(Some(&encoded)),
}
redirect_uri
@ -1873,15 +1878,31 @@ impl IdmServerProxyReadTransaction<'_> {
admin_warn!("Unsupported OAuth2 response_type (should be 'code')");
return Err(Oauth2Error::UnsupportedResponseType);
}
let Some(response_mode) = auth_req.get_response_mode() else {
admin_warn!(
warn!(
"Invalid response_mode {:?} for response_type {:?}",
auth_req.response_mode,
auth_req.response_type
auth_req.response_mode, auth_req.response_type
);
return Err(Oauth2Error::InvalidRequest);
};
let response_mode = match response_mode {
ResponseMode::Query => SupportedResponseMode::Query,
ResponseMode::Fragment => SupportedResponseMode::Fragment,
ResponseMode::FormPost => {
warn!(
"Invalid response mode form_post requested - many clients request this incorrectly but proceed with response_mode=query. Remapping to query."
);
warn!("This behaviour WILL BE REMOVED in a future release.");
SupportedResponseMode::Query
}
ResponseMode::Invalid => {
warn!("Invalid response mode requested, unable to proceed");
return Err(Oauth2Error::InvalidRequest);
}
};
/*
* 4.1.2.1. Error Response
*

View file

@ -14,87 +14,73 @@ const ALLOWED_ATTRIBUTES: &[&str] = &[
"role",
"output_mode",
"log_level",
"ldap",
];
fn parse_knobs(
input: &syn::ItemFn,
server_config: &Punctuated<ExprAssign, syn::token::Comma>,
) -> TokenStream {
// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, _last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
// `syn::Error::new_spanned` does.
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
let end = last_stmt.last().map_or(start, |t| t.span());
(start, end)
};
#[derive(Default)]
struct Flags {
ldap: bool,
}
// here we gather all the provided configuration in a struct like declaration
// By now we have already checked that the configurations provided belong to the allowed subset
let mut field_modifications = quote! {};
server_config.pairs().for_each(|p| {
let field_name = p.value().left.to_token_stream(); // here we can use to_token_stream as we know we're iterating over ExprAssigns
let field_value = p.value().right.to_token_stream();
field_modifications.extend(quote! {
#field_name: #field_value,})
fn parse_attributes(
args: &TokenStream,
input: &syn::ItemFn,
) -> Result<(proc_macro2::TokenStream, Flags), syn::Error> {
let args: Punctuated<ExprAssign, syn::token::Comma> =
Punctuated::<ExprAssign, Token![,]>::parse_terminated.parse(args.clone())?;
let args_are_allowed = args.pairs().all(|p| {
ALLOWED_ATTRIBUTES.to_vec().contains(
&p.value()
.left
.span()
.source_text()
.unwrap_or_default()
.as_str(),
)
});
// Setup the config filling the remaining fields with the default values
let default_config_struct = quote!(kanidmd_core::config::Configuration {
if !args_are_allowed {
let msg = "Invalid test config attribute. The following are allowed";
return Err(syn::Error::new_spanned(
input.sig.fn_token,
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),
));
}
let mut flags = Flags::default();
let mut field_modifications = quote! {};
args.pairs().for_each(|p| {
match p
.value()
.left
.span()
.source_text()
.unwrap_or_default()
.as_str()
{
"ldap" => {
flags.ldap = true;
field_modifications.extend(quote! {
ldapaddress: Some("on".to_string()),})
}
_ => {
let field_name = p.value().left.to_token_stream(); // here we can use to_token_stream as we know we're iterating over ExprAssigns
let field_value = p.value().right.to_token_stream();
// This is printing out struct members.
field_modifications.extend(quote! {
#field_name: #field_value,})
}
}
});
let ts = quote!(kanidmd_core::config::Configuration {
#field_modifications
..kanidmd_core::config::Configuration::new_for_test()
});
let rt = quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_current_thread()
};
let header = quote! {
#[::core::prelude::v1::test]
};
let fn_name = &input.sig.ident;
let test_driver = Ident::new(&format!("tk_{}", fn_name), input.sig.span());
// Effectively we are just injecting a real test function around this which we will
// call.
let result = quote! {
#input
#header
fn #test_driver() {
let body = async {
let (rsclient, mut core_handle) = kanidmd_testkit::setup_async_test(#default_config_struct).await;
#fn_name(rsclient).await;
core_handle.shutdown().await;
};
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return #rt
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(body);
}
}
};
result.into()
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(TokenStream::from(error.into_compile_error()));
tokens
Ok((ts, flags))
}
pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
@ -115,31 +101,80 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
let msg = "the `async` keyword is missing from the function declaration";
return token_stream_with_error(item, syn::Error::new_spanned(input.sig.fn_token, msg));
}
let args: Punctuated<ExprAssign, syn::token::Comma> =
match Punctuated::<ExprAssign, Token![,]>::parse_terminated.parse(args.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(args, e),
};
let args_are_allowed = args.pairs().all(|p| {
ALLOWED_ATTRIBUTES.to_vec().contains(
&p.value()
.left
.span()
.source_text()
.unwrap_or_default()
.as_str(),
)
});
if !args_are_allowed {
let msg =
"Currently only a subset of all the server configs can be set. Here is the full list";
return token_stream_with_error(
item,
syn::Error::new_spanned(
input.sig.fn_token,
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),
),
);
}
parse_knobs(&input, &args)
// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, _last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
// `syn::Error::new_spanned` does.
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
let end = last_stmt.last().map_or(start, |t| t.span());
(start, end)
};
// Setup the config filling the remaining fields with the default values
let (default_config_struct, flags) = match parse_attributes(&args, &input) {
Ok(dc) => dc,
Err(e) => return token_stream_with_error(args, e),
};
let rt = quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_current_thread()
};
let header = quote! {
#[::core::prelude::v1::test]
};
let test_fn_args = if flags.ldap {
quote! {
&test_env
}
} else {
quote! {
&test_env.rsclient
}
};
let test_fn = &input.sig.ident;
let test_driver = Ident::new(&format!("tk_{}", test_fn), input.sig.span());
// Effectively we are just injecting a real test function around this which we will
// call.
let result = quote! {
#input
#header
fn #test_driver() {
let body = async {
let mut test_env = kanidmd_testkit::setup_async_test(#default_config_struct).await;
#test_fn(#test_fn_args).await;
test_env.core_handle.shutdown().await;
};
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return #rt
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(body);
}
}
};
result.into()
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(TokenStream::from(error.into_compile_error()));
tokens
}

View file

@ -53,6 +53,7 @@ escargot = "0.5.13"
# used for webdriver testing
fantoccini = { version = "0.21.4" }
futures = { workspace = true }
ldap3_client = { workspace = true }
oauth2_ext = { workspace = true, default-features = false, features = [
"reqwest",
] }

View file

@ -10,16 +10,16 @@
#![deny(clippy::needless_pass_by_value)]
#![deny(clippy::trivially_copy_pass_by_ref)]
use std::net::TcpStream;
use std::sync::atomic::{AtomicU16, Ordering};
use kanidm_client::{KanidmClient, KanidmClientBuilder};
use kanidm_proto::internal::{Filter, Modify, ModifyList};
use kanidmd_core::config::{Configuration, IntegrationTestConfig};
use kanidmd_core::{create_server_core, CoreHandle};
use kanidmd_lib::prelude::{Attribute, NAME_SYSTEM_ADMINS};
use std::net::TcpStream;
use std::sync::atomic::{AtomicU16, Ordering};
use tokio::task;
use tracing::error;
use url::Url;
pub const ADMIN_TEST_USER: &str = "admin";
pub const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
@ -46,14 +46,9 @@ pub fn is_free_port(port: u16) -> bool {
}
// Test external behaviours of the service.
// allowed because the use of this function is behind a test gate
#[allow(dead_code)]
pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreHandle) {
sketching::test_init();
fn port_loop() -> u16 {
let mut counter = 0;
let port = loop {
loop {
let possible_port = PORT_ALLOC.fetch_add(1, Ordering::SeqCst);
if is_free_port(possible_port) {
break possible_port;
@ -64,7 +59,21 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
tracing::error!("Unable to allocate port!");
panic!();
}
};
}
}
pub struct AsyncTestEnvironment {
pub rsclient: KanidmClient,
pub core_handle: CoreHandle,
pub ldap_url: Option<Url>,
}
// allowed because the use of this function is behind a test gate
#[allow(dead_code)]
pub async fn setup_async_test(mut config: Configuration) -> AsyncTestEnvironment {
sketching::test_init();
let port = port_loop();
let int_config = Box::new(IntegrationTestConfig {
admin_user: ADMIN_TEST_USER.to_string(),
@ -75,6 +84,16 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
let addr = format!("http://localhost:{}", port);
let ldap_url = if config.ldapaddress.is_some() {
let ldapport = port_loop();
config.ldapaddress = Some(format!("127.0.0.1:{}", ldapport));
Url::parse(&format!("ldap://127.0.0.1:{}", ldapport))
.inspect_err(|err| error!(?err, "ldap address setup"))
.ok()
} else {
None
};
// Setup the address and origin..
config.address = format!("127.0.0.1:{}", port);
config.integration_test_config = Some(int_config);
@ -102,7 +121,11 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
tracing::info!("Testkit server setup complete - {}", addr);
(rsclient, core_handle)
AsyncTestEnvironment {
rsclient,
core_handle,
ldap_url,
}
}
/// creates a user (username: `id`) and puts them into a group, creating it if need be.

View file

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use tracing::info;
#[kanidmd_testkit::test]
async fn check_that_the_swagger_api_loads(rsclient: kanidm_client::KanidmClient) {
async fn check_that_the_swagger_api_loads(rsclient: &kanidm_client::KanidmClient) {
#[derive(Serialize, Deserialize, Debug)]
struct OpenAPIResponse {
pub openapi: String,

View file

@ -3,7 +3,7 @@ use kanidm_proto::constants::ATTR_DOMAIN_DISPLAY_NAME;
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
#[kanidmd_testkit::test]
async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: KanidmClient) {
async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: &KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
@ -13,8 +13,9 @@ async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: KanidmClient) {
.await
.expect("Failed to set LDAP allow unix password bind to true");
}
#[kanidmd_testkit::test]
async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
@ -27,7 +28,7 @@ async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
@ -40,7 +41,7 @@ async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_idm_domain_set_display_name(rsclient: KanidmClient) {
async fn test_idm_domain_set_display_name(rsclient: &KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await

View file

@ -4,7 +4,7 @@ use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
use serde_json::Value;
#[kanidmd_testkit::test]
async fn test_v1_group_id_patch(rsclient: KanidmClient) {
async fn test_v1_group_id_patch(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -25,7 +25,7 @@ async fn test_v1_group_id_patch(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_v1_group_id_attr_post(rsclient: KanidmClient) {
async fn test_v1_group_id_attr_post(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;

View file

@ -1,7 +1,7 @@
use kanidm_client::{http::header, KanidmClient};
#[kanidmd_testkit::test]
async fn test_https_manifest(rsclient: KanidmClient) {
async fn test_https_manifest(rsclient: &KanidmClient) {
// We need to do manual reqwests here.
let client = rsclient.client();

View file

@ -11,7 +11,7 @@ const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
// *test where we don't trust the x-forwarded-for header
#[kanidmd_testkit::test(trust_x_forward_for = false)]
async fn dont_trust_xff_send_header(rsclient: KanidmClient) {
async fn dont_trust_xff_send_header(rsclient: &KanidmClient) {
let client = rsclient.client();
let res = client
@ -32,7 +32,7 @@ async fn dont_trust_xff_send_header(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test(trust_x_forward_for = false)]
async fn dont_trust_xff_dont_send_header(rsclient: KanidmClient) {
async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) {
let client = rsclient.client();
let res = client
@ -58,7 +58,7 @@ async fn dont_trust_xff_dont_send_header(rsclient: KanidmClient) {
// *test where we trust the x-forwarded-for header
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_send_invalid_header_single_value(rsclient: KanidmClient) {
async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) {
let client = rsclient.client();
let res = client
@ -78,7 +78,7 @@ async fn trust_xff_send_invalid_header_single_value(rsclient: KanidmClient) {
// with a valid leftmost address and an invalid address later in the list. Right now it wouldn't work.
//
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_send_invalid_header_multiple_values(rsclient: KanidmClient) {
async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) {
let client = rsclient.client();
let res = client
@ -95,7 +95,7 @@ async fn trust_xff_send_invalid_header_multiple_values(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: KanidmClient) {
async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) {
let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
let client = rsclient.client();
@ -115,7 +115,7 @@ async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: KanidmClient)
}
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: KanidmClient) {
async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) {
let ip_addr = "203.0.113.195";
let client = rsclient.client();
@ -135,7 +135,7 @@ async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: KanidmClient)
}
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_send_valid_header_multiple_address(rsclient: KanidmClient) {
async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) {
let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
let client = rsclient.client();
@ -176,7 +176,7 @@ async fn trust_xff_send_valid_header_multiple_address(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test(trust_x_forward_for = true)]
async fn trust_xff_dont_send_header(rsclient: KanidmClient) {
async fn trust_xff_dont_send_header(rsclient: &KanidmClient) {
let client = rsclient.client();
let res = client

View file

@ -2,7 +2,7 @@ use kanidm_client::http::header;
use kanidm_client::KanidmClient;
#[kanidmd_testkit::test]
async fn test_https_middleware_headers(rsclient: KanidmClient) {
async fn test_https_middleware_headers(rsclient: &KanidmClient) {
// We need to do manual reqwests here.
let client = rsclient.client();

View file

@ -15,7 +15,7 @@ static USER_B_NAME: &str = "valid_user_b";
// These tests check that invalid requests return the expected error
#[kanidmd_testkit::test]
async fn test_not_authenticated(rsclient: KanidmClient) {
async fn test_not_authenticated(rsclient: &KanidmClient) {
// basically here we try a bit of all the possible combinations while unauthenticated to check it's not working
setup_server(&rsclient).await;
create_user(&rsclient, USER_A_NAME).await;
@ -46,7 +46,7 @@ async fn test_not_authenticated(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_non_existing_user_id(rsclient: KanidmClient) {
async fn test_non_existing_user_id(rsclient: &KanidmClient) {
setup_server(&rsclient).await;
create_user(&rsclient, USER_A_NAME).await;
create_user(&rsclient, USER_B_NAME).await;
@ -86,7 +86,7 @@ async fn test_non_existing_user_id(rsclient: KanidmClient) {
// error cases have already been tested in the previous section!
// Each tests is named like `test_{api input}_response_{expected api output}_or_{expected api output}`
#[kanidmd_testkit::test]
async fn test_start_response_identity_verification_available(rsclient: KanidmClient) {
async fn test_start_response_identity_verification_available(rsclient: &KanidmClient) {
setup_server(&rsclient).await;
create_user(&rsclient, USER_A_NAME).await;
login_with_user(&rsclient, USER_A_NAME).await;
@ -105,7 +105,7 @@ async fn test_start_response_identity_verification_available(rsclient: KanidmCli
// this function tests both possible POSITIVE outcomes if we start from
// `Start`, that is WaitForCode or ProvideCode
#[kanidmd_testkit::test]
async fn test_start_response_wait_for_code_or_provide_code(rsclient: KanidmClient) {
async fn test_start_response_wait_for_code_or_provide_code(rsclient: &KanidmClient) {
setup_server(&rsclient).await;
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
@ -129,7 +129,7 @@ async fn test_start_response_wait_for_code_or_provide_code(rsclient: KanidmClien
}
#[kanidmd_testkit::test]
async fn test_provide_code_response_code_failure_or_provide_code(rsclient: KanidmClient) {
async fn test_provide_code_response_code_failure_or_provide_code(rsclient: &KanidmClient) {
setup_server(&rsclient).await;
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
@ -157,7 +157,7 @@ async fn test_provide_code_response_code_failure_or_provide_code(rsclient: Kanid
// here we actually test the full idm flow by duplicating the server
#[kanidmd_testkit::test]
async fn test_full_identification_flow(rsclient: KanidmClient) {
async fn test_full_identification_flow(rsclient: &KanidmClient) {
setup_server(&rsclient).await;
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
@ -175,12 +175,12 @@ async fn test_full_identification_flow(rsclient: KanidmClient) {
(
valid_user_a_client,
USER_A_NAME,
valid_user_b_client,
&valid_user_b_client,
USER_B_NAME,
)
} else {
(
valid_user_b_client,
&valid_user_b_client,
USER_B_NAME,
valid_user_a_client,
USER_A_NAME,

View file

@ -66,7 +66,7 @@ async fn get_webdriver_client() -> fantoccini::Client {
#[kanidmd_testkit::test]
#[cfg(feature = "webdriver")]
async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
async fn test_webdriver_user_login(rsclient: &KanidmClient) {
if !cfg!(feature = "webdriver") {
println!("Skipping test as webdriver feature is not enabled!");
return;
@ -206,7 +206,7 @@ async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_domain_reset_token_key(rsclient: KanidmClient) {
async fn test_domain_reset_token_key(rsclient: &KanidmClient) {
login_put_admin_idm_admins(&rsclient).await;
let token = rsclient.get_token().await.expect("No bearer token present");
@ -219,7 +219,7 @@ async fn test_domain_reset_token_key(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
login_put_admin_idm_admins(&rsclient).await;
assert!(rsclient
.idm_domain_set_ldap_basedn("dc=krabsarekool,dc=example,dc=com")
@ -232,7 +232,7 @@ async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
login_put_admin_idm_admins(&rsclient).await;
assert!(rsclient
.idm_domain_set_ldap_max_queryable_attrs(20)
@ -246,7 +246,7 @@ async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
#[kanidmd_testkit::test]
/// Checks that a built-in group idm_all_persons has the "builtin" class as expected.
async fn test_all_persons_has_builtin_class(rsclient: KanidmClient) {
async fn test_all_persons_has_builtin_class(rsclient: &KanidmClient) {
login_put_admin_idm_admins(&rsclient).await;
let res = rsclient
.idm_group_get("idm_all_persons")

View file

@ -0,0 +1,16 @@
use kanidmd_testkit::AsyncTestEnvironment;
use ldap3_client::LdapClientBuilder;
#[kanidmd_testkit::test(ldap = true)]
async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
let ldap_url = test_env.ldap_url.as_ref().unwrap();
let mut ldap_client = LdapClientBuilder::new(ldap_url).build().await.unwrap();
// Bind as anonymous
ldap_client.bind("".into(), "".into()).await.unwrap();
let whoami = ldap_client.whoami().await.unwrap();
assert_eq!(whoami, Some("u: anonymous@localhost".to_string()));
}

View file

@ -6,6 +6,7 @@ mod https_extractors;
mod https_middleware;
mod identity_verification_tests;
mod integration;
mod ldap_basic;
mod mtls_test;
mod oauth2_test;
mod person;

View file

@ -40,7 +40,7 @@ use kanidmd_testkit::{
/// If `true`, use the `code` passed in the callback URI's fragment, and
/// require the query parameter to be empty.
async fn test_oauth2_openid_basic_flow_impl(
rsclient: KanidmClient,
rsclient: &KanidmClient,
response_mode: Option<&str>,
response_in_fragment: bool,
) {
@ -535,7 +535,7 @@ async fn test_oauth2_openid_basic_flow_impl(
///
/// The response should be returned as a query parameter.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: KanidmClient) {
async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: &KanidmClient) {
test_oauth2_openid_basic_flow_impl(rsclient, None, false).await;
}
@ -544,7 +544,7 @@ async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: KanidmClient) {
///
/// The response should be returned as a query parameter.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_basic_flow_mode_query(rsclient: KanidmClient) {
async fn test_oauth2_openid_basic_flow_mode_query(rsclient: &KanidmClient) {
test_oauth2_openid_basic_flow_impl(rsclient, Some("query"), false).await;
}
@ -553,7 +553,7 @@ async fn test_oauth2_openid_basic_flow_mode_query(rsclient: KanidmClient) {
///
/// The response should be returned in the URI's fragment.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: KanidmClient) {
async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: &KanidmClient) {
test_oauth2_openid_basic_flow_impl(rsclient, Some("fragment"), true).await;
}
@ -570,7 +570,7 @@ async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: KanidmClient) {
/// If `true`, use the `code` passed in the callback URI's fragment, and
/// require the query parameter to be empty.
async fn test_oauth2_openid_public_flow_impl(
rsclient: KanidmClient,
rsclient: &KanidmClient,
response_mode: Option<&str>,
response_in_fragment: bool,
) {
@ -901,7 +901,7 @@ async fn test_oauth2_openid_public_flow_impl(
///
/// The response should be returned as a query parameter.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_public_flow_mode_unset(rsclient: KanidmClient) {
async fn test_oauth2_openid_public_flow_mode_unset(rsclient: &KanidmClient) {
test_oauth2_openid_public_flow_impl(rsclient, None, false).await;
}
@ -910,7 +910,7 @@ async fn test_oauth2_openid_public_flow_mode_unset(rsclient: KanidmClient) {
///
/// The response should be returned as a query parameter.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_public_flow_mode_query(rsclient: KanidmClient) {
async fn test_oauth2_openid_public_flow_mode_query(rsclient: &KanidmClient) {
test_oauth2_openid_public_flow_impl(rsclient, Some("query"), false).await;
}
@ -919,12 +919,12 @@ async fn test_oauth2_openid_public_flow_mode_query(rsclient: KanidmClient) {
///
/// The response should be returned in the URI's fragment.
#[kanidmd_testkit::test]
async fn test_oauth2_openid_public_flow_mode_fragment(rsclient: KanidmClient) {
async fn test_oauth2_openid_public_flow_mode_fragment(rsclient: &KanidmClient) {
test_oauth2_openid_public_flow_impl(rsclient, Some("fragment"), true).await;
}
#[kanidmd_testkit::test]
async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
async fn test_oauth2_token_post_bad_bodies(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -960,7 +960,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
async fn test_oauth2_token_revoke_post(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;

View file

@ -4,7 +4,7 @@ use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
use serde_json::Value;
#[kanidmd_testkit::test]
async fn test_v1_person_id_patch(rsclient: KanidmClient) {
async fn test_v1_person_id_patch(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -25,7 +25,7 @@ async fn test_v1_person_id_patch(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_v1_person_id_ssh_pubkeys_post(rsclient: KanidmClient) {
async fn test_v1_person_id_ssh_pubkeys_post(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;

View file

@ -29,7 +29,7 @@ use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
const UNIX_TEST_PASSWORD: &str = "unix test user password";
#[kanidmd_testkit::test]
async fn test_server_create(rsclient: KanidmClient) {
async fn test_server_create(rsclient: &KanidmClient) {
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
@ -55,7 +55,7 @@ async fn test_server_create(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_whoami_anonymous(rsclient: KanidmClient) {
async fn test_server_whoami_anonymous(rsclient: &KanidmClient) {
// First show we are un-authenticated.
let pre_res = rsclient.whoami().await;
// This means it was okay whoami, but no uat attached.
@ -84,7 +84,7 @@ async fn test_server_whoami_anonymous(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_whoami_admin_simple_password(rsclient: KanidmClient) {
async fn test_server_whoami_admin_simple_password(rsclient: &KanidmClient) {
// First show we are un-authenticated.
let pre_res = rsclient.whoami().await;
// This means it was okay whoami, but no uat attached.
@ -109,7 +109,7 @@ async fn test_server_whoami_admin_simple_password(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_search(rsclient: KanidmClient) {
async fn test_server_search(rsclient: &KanidmClient) {
// First show we are un-authenticated.
let pre_res = rsclient.whoami().await;
// This means it was okay whoami, but no uat attached.
@ -135,7 +135,7 @@ async fn test_server_search(rsclient: KanidmClient) {
// test the rest group endpoint.
#[kanidmd_testkit::test]
async fn test_server_rest_group_read(rsclient: KanidmClient) {
async fn test_server_rest_group_read(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -151,7 +151,7 @@ async fn test_server_rest_group_read(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_group_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -263,7 +263,7 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_account_read(rsclient: KanidmClient) {
async fn test_server_rest_account_read(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -279,7 +279,7 @@ async fn test_server_rest_account_read(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_schema_read(rsclient: KanidmClient) {
async fn test_server_rest_schema_read(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -313,7 +313,7 @@ async fn test_server_rest_schema_read(rsclient: KanidmClient) {
// Test resetting a radius cred, and then checking/viewing it.
#[kanidmd_testkit::test]
async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
async fn test_server_radius_credential_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -384,7 +384,7 @@ async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_person_account_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -439,7 +439,7 @@ async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_sshkey_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_sshkey_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -509,7 +509,7 @@ async fn test_server_rest_sshkey_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_domain_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_domain_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -539,7 +539,7 @@ async fn test_server_rest_domain_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_posix_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -660,7 +660,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_posix_auth_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -760,7 +760,7 @@ async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_recycle_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -814,7 +814,7 @@ async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
async fn test_server_rest_oauth2_basic_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -1027,7 +1027,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
async fn test_server_credential_update_session_pw(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -1102,7 +1102,7 @@ async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_credential_update_session_totp_pw(rsclient: KanidmClient) {
async fn test_server_credential_update_session_totp_pw(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
@ -1365,7 +1365,7 @@ async fn setup_demo_account_password(
}
#[kanidmd_testkit::test]
async fn test_server_credential_update_session_passkey(rsclient: KanidmClient) {
async fn test_server_credential_update_session_passkey(rsclient: &KanidmClient) {
let mut wa = setup_demo_account_passkey(&rsclient).await;
let res = rsclient
@ -1383,7 +1383,7 @@ async fn test_server_credential_update_session_passkey(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
async fn test_server_api_token_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -1566,7 +1566,7 @@ async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_user_auth_token_lifecycle(rsclient: KanidmClient) {
async fn test_server_user_auth_token_lifecycle(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -1689,7 +1689,7 @@ async fn test_server_user_auth_token_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_user_auth_reauthentication(rsclient: KanidmClient) {
async fn test_server_user_auth_reauthentication(rsclient: &KanidmClient) {
let mut wa = setup_demo_account_passkey(&rsclient).await;
let res = rsclient
@ -1868,7 +1868,7 @@ async fn start_password_session(
}
#[kanidmd_testkit::test]
async fn test_server_user_auth_unprivileged(rsclient: KanidmClient) {
async fn test_server_user_auth_unprivileged(rsclient: &KanidmClient) {
let (account_name, account_pass) = setup_demo_account_password(&rsclient)
.await
.expect("Failed to setup demo_account");
@ -1891,7 +1891,7 @@ async fn test_server_user_auth_unprivileged(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_server_user_auth_privileged_shortcut(rsclient: KanidmClient) {
async fn test_server_user_auth_privileged_shortcut(rsclient: &KanidmClient) {
let (account_name, account_pass) = setup_demo_account_password(&rsclient)
.await
.expect("Failed to setup demo_account");

View file

@ -9,7 +9,7 @@ use std::str::FromStr;
use url::Url;
#[kanidmd_testkit::test]
async fn test_sync_account_lifecycle(rsclient: KanidmClient) {
async fn test_sync_account_lifecycle(rsclient: &KanidmClient) {
let a_res = rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await;
@ -104,7 +104,7 @@ async fn test_sync_account_lifecycle(rsclient: KanidmClient) {
}
#[kanidmd_testkit::test]
async fn test_scim_sync_entry_get(rsclient: KanidmClient) {
async fn test_scim_sync_entry_get(rsclient: &KanidmClient) {
let res = rsclient
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;

View file

@ -2,7 +2,7 @@ use kanidm_client::KanidmClient;
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
#[kanidmd_testkit::test]
async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) {
async fn test_v1_service_account_id_attr_attr_delete(rsclient: &KanidmClient) {
// We need to do manual reqwests here.
let client = rsclient.client();

View file

@ -2,7 +2,7 @@ use kanidm_client::KanidmClient;
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
#[kanidmd_testkit::test]
async fn test_v1_system_post_attr(rsclient: KanidmClient) {
async fn test_v1_system_post_attr(rsclient: &KanidmClient) {
let client = rsclient.client();
let response = match client

View file

@ -3,8 +3,8 @@ use kanidmd_lib::constants::NAME_IDM_ADMINS;
use kanidmd_testkit::*;
#[kanidmd_testkit::test]
async fn account_id_unix_token(rsclient: KanidmClient) {
login_put_admin_idm_admins(&rsclient).await;
async fn account_id_unix_token(rsclient: &KanidmClient) {
login_put_admin_idm_admins(rsclient).await;
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
// create test user without creating new groups