| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- """The env-managed provider is read-only through the API (#2593).
- Startup rewrites this row from BAMBUDDY_OIDC_* on every boot, so a UI edit
- would silently disappear at the next restart -- the operator would see their
- change accepted and then reverted, with nothing explaining why. Refusing the
- write is the honest answer.
- Locking it out is safe because BAMBUDDY_LOCAL_LOGIN (#1589) is the documented
- recovery path if the provider itself becomes unusable.
- """
- from __future__ import annotations
- import pytest
- from httpx import AsyncClient
- from backend.app.models.oidc_provider import OIDCProvider
- from backend.tests.integration.test_mfa_api import _auth_header, _setup_and_login
- async def _env_managed_provider(db_session) -> int:
- provider = OIDCProvider(
- name="Env Keycloak",
- issuer_url="https://sso.example.com/realms/main",
- client_id="bambuddy",
- icon_url="https://sso.example.com/logo.png",
- is_env_managed=True,
- )
- provider.client_secret = "s3cr3t"
- db_session.add(provider)
- await db_session.commit()
- await db_session.refresh(provider)
- return provider.id
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_put_is_refused(async_client: AsyncClient, db_session):
- provider_id = await _env_managed_provider(db_session)
- token = await _setup_and_login(async_client, "envlockput", "envlockput123")
- response = await async_client.put(
- f"/api/v1/auth/oidc/providers/{provider_id}",
- json={"name": "hijacked"},
- headers=_auth_header(token),
- )
- assert response.status_code == 409
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_delete_is_refused(async_client: AsyncClient, db_session):
- provider_id = await _env_managed_provider(db_session)
- token = await _setup_and_login(async_client, "envlockdel", "envlockdel123")
- response = await async_client.delete(
- f"/api/v1/auth/oidc/providers/{provider_id}",
- headers=_auth_header(token),
- )
- assert response.status_code == 409
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_icon_delete_is_refused(async_client: AsyncClient, db_session):
- """The icon is part of the env config too -- BAMBUDDY_OIDC_ICON_URL."""
- provider_id = await _env_managed_provider(db_session)
- token = await _setup_and_login(async_client, "envlockicondel", "envlockicondel123")
- response = await async_client.delete(
- f"/api/v1/auth/oidc/providers/{provider_id}/icon",
- headers=_auth_header(token),
- )
- assert response.status_code == 409
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_icon_refresh_is_refused(async_client: AsyncClient, db_session):
- provider_id = await _env_managed_provider(db_session)
- token = await _setup_and_login(async_client, "envlockiconref", "envlockiconref123")
- response = await async_client.post(
- f"/api/v1/auth/oidc/providers/{provider_id}/icon/refresh",
- headers=_auth_header(token),
- )
- assert response.status_code == 409
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_a_ui_provider_is_still_editable(async_client: AsyncClient):
- """The lock must not leak onto providers the operator created themselves --
- they coexist with the env one and stay fully editable."""
- token = await _setup_and_login(async_client, "envlockui", "envlockui123")
- created = await async_client.post(
- "/api/v1/auth/oidc/providers",
- json={
- "name": "UI provider",
- "issuer_url": "https://other.example.com",
- "client_id": "ui",
- "client_secret": "ui-secret",
- "scopes": "openid",
- "is_enabled": True,
- "auto_create_users": False,
- },
- headers=_auth_header(token),
- )
- provider_id = created.json()["id"]
- response = await async_client.put(
- f"/api/v1/auth/oidc/providers/{provider_id}",
- json={"name": "Renamed"},
- headers=_auth_header(token),
- )
- assert response.status_code == 200
- assert response.json()["name"] == "Renamed"
- @pytest.mark.asyncio
- @pytest.mark.integration
- async def test_the_response_says_which_provider_is_env_managed(async_client: AsyncClient, db_session):
- """The frontend needs this to render the lock; without it the UI would show
- editable fields whose writes the API then refuses."""
- await _env_managed_provider(db_session)
- token = await _setup_and_login(async_client, "envlockflag", "envlockflag123")
- response = await async_client.get("/api/v1/auth/oidc/providers/all", headers=_auth_header(token))
- assert response.status_code == 200
- providers = response.json()
- assert any(p["is_env_managed"] for p in providers)
|