diff --git a/.bumpversion.toml b/.bumpversion.toml index 61b2b7a0..9c5b5d2d 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "1.20.0+261027" +current_version = "1.20.1+261112" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)\\+(?P\\d+)" serialize = ["{major}.{minor}.{patch}+{build}"] search = "{current_version}" diff --git a/pyproject.toml b/pyproject.toml index 3bb2ac16..f4aa878e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rasenmaeher_api" -version = "1.20.0+261027" +version = "1.20.1+261112" description = "python-rasenmaeher-api" authors = [ { name = "Aciid", email = "703382+Aciid@users.noreply.github.com" }, diff --git a/src/rasenmaeher_api/__init__.py b/src/rasenmaeher_api/__init__.py index 7457e8a8..f4e020ff 100644 --- a/src/rasenmaeher_api/__init__.py +++ b/src/rasenmaeher_api/__init__.py @@ -1,3 +1,3 @@ """python-rasenmaeher-api""" -__version__ = "1.20.0+261027" # NOTE Use `bump-my-version` to bump versions correctly +__version__ = "1.20.1+261112" # NOTE Use `bump-my-version` to bump versions correctly diff --git a/src/rasenmaeher_api/web/api/federation/views.py b/src/rasenmaeher_api/web/api/federation/views.py index 8e2eba19..59e3c760 100644 --- a/src/rasenmaeher_api/web/api/federation/views.py +++ b/src/rasenmaeher_api/web/api/federation/views.py @@ -9,6 +9,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.exceptions import RequestValidationError from fastapi.routing import APIRoute +from libpvarki.middleware.mtlsheader import CONFIG from starlette.responses import Response from rasenmaeher_api.mtlsinit import get_session_winit @@ -53,8 +54,10 @@ async def dispatch[Response: (FederationTrustResponse, FederationOutgoingRespons if not isinstance(base, str): raise TypeError("Invalid product API") parsed = urlsplit(base) + # Meshed (Linkerd) product APIs are internal http, authenticated by l5d identity. + schemes = ("https", "http") if CONFIG("MTLS_TRUST_L5D", cast=bool, default=False) else ("https",) if ( - parsed.scheme != "https" + parsed.scheme not in schemes or not parsed.hostname or parsed.username or parsed.password diff --git a/tests/federation/test_dispatch.py b/tests/federation/test_dispatch.py index 953bcc1c..c15109e9 100644 --- a/tests/federation/test_dispatch.py +++ b/tests/federation/test_dispatch.py @@ -183,7 +183,38 @@ def test_missing_product(public: TestClient, product: MagicMock, issuer_cl: Issu 123, ], ) -def test_bad_product_config(public: TestClient, product: MagicMock, issuer_cl: Issuer, api: Any) -> None: +def test_bad_product_config( + public: TestClient, product: MagicMock, issuer_cl: Issuer, monkeypatch: pytest.MonkeyPatch, api: Any +) -> None: + monkeypatch.delenv("MTLS_TRUST_L5D", raising=False) + RMSettings.singleton().kraftwerk_manifest_dict["products"]["tak"]["api"] = api + assert post(public, issuer_cl).status_code == 503 + product.post.assert_not_called() + + +def test_meshed_http_dispatch( + public: TestClient, product: MagicMock, issuer_cl: Issuer, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setenv("MTLS_TRUST_L5D", "true") + RMSettings.singleton().kraftwerk_manifest_dict["products"]["tak"]["api"] = ( + "http://tak-server.app-tak.svc.cluster.local:8003/" + ) + assert post(public, issuer_cl).status_code == 200 + assert product.post.call_args.args == ("http://tak-server.app-tak.svc.cluster.local:8003/api/v1/federation/trust",) + product.post.assert_called_once() + + +@pytest.mark.parametrize( + "api", + [ + "https://user:pass@tak.local.example/", # pragma: allowlist secret + 123, + ], +) +def test_meshed_bad_product_config( + public: TestClient, product: MagicMock, issuer_cl: Issuer, monkeypatch: pytest.MonkeyPatch, api: Any +) -> None: + monkeypatch.setenv("MTLS_TRUST_L5D", "true") RMSettings.singleton().kraftwerk_manifest_dict["products"]["tak"]["api"] = api assert post(public, issuer_cl).status_code == 503 product.post.assert_not_called() diff --git a/tests/test_rasenmaeher_api.py b/tests/test_rasenmaeher_api.py index 165506b7..8d4b94c7 100644 --- a/tests/test_rasenmaeher_api.py +++ b/tests/test_rasenmaeher_api.py @@ -15,7 +15,7 @@ def test_version() -> None: """Make sure version matches expected""" - assert __version__ == "1.20.0+261027" + assert __version__ == "1.20.1+261112" @pytest.mark.asyncio(loop_scope="session") diff --git a/uv.lock b/uv.lock index 0d2de38f..ec647301 100644 --- a/uv.lock +++ b/uv.lock @@ -2366,7 +2366,7 @@ wheels = [ [[package]] name = "rasenmaeher-api" -version = "1.20.0+261027" +version = "1.20.1+261112" source = { editable = "." } dependencies = [ { name = "aiodns" },