Compare commits

..

1 Commits

Author SHA1 Message Date
renovate-bot-cbcoutinho[bot] 68caf5f3d0 chore(deps): update docker.io/library/python:3.12-slim-trixie docker digest to 3d5ed97 2026-03-20 05:21:42 +00:00
4 changed files with 18 additions and 148 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
FROM docker.io/library/python:3.12-slim-trixie@sha256:f3fa41d74a768c2fce8016b98c191ae8c1bacd8f1152870a3f9f87d350920b7c FROM docker.io/library/python:3.12-slim-trixie@sha256:3d5ed973e45820f5ba5e46bd065bd88b3a504ff0724d85980dcd05eab361fcf4
COPY --from=ghcr.io/astral-sh/uv:0.10.7@sha256:edd1fd89f3e5b005814cc8f777610445d7b7e3ed05361f9ddfae67bebfe8456a /uv /uvx /bin/ COPY --from=ghcr.io/astral-sh/uv:0.10.7@sha256:edd1fd89f3e5b005814cc8f777610445d7b7e3ed05361f9ddfae67bebfe8456a /uv /uvx /bin/
+1 -1
View File
@@ -12,7 +12,7 @@
# - Per-session app password authentication # - Per-session app password authentication
# - Multi-user support via Smithery session config # - Multi-user support via Smithery session config
FROM docker.io/library/python:3.12-slim-trixie@sha256:f3fa41d74a768c2fce8016b98c191ae8c1bacd8f1152870a3f9f87d350920b7c FROM docker.io/library/python:3.12-slim-trixie@sha256:3d5ed973e45820f5ba5e46bd065bd88b3a504ff0724d85980dcd05eab361fcf4
WORKDIR /app WORKDIR /app
+15 -17
View File
@@ -235,26 +235,24 @@ async def get_server_status(request: Request) -> JSONResponse:
if mode == AuthMode.MULTI_USER_BASIC: if mode == AuthMode.MULTI_USER_BASIC:
response_data["supports_app_passwords"] = settings.enable_offline_access response_data["supports_app_passwords"] = settings.enable_offline_access
# Include OIDC configuration for client discovery (e.g. Astrolabe PHP app). # Include OIDC configuration if OAuth is available
# Always attempt to provide oidc.discovery_url so clients can discover the # This includes OAuth mode AND hybrid mode (multi_user_basic + offline_access)
# IdP regardless of the current auth mode. This enables smoother transitions # Astrolabe needs OIDC config to discover IdP for OAuth flow in hybrid mode
# between auth modes and lets Astrolabe pre-discover OIDC endpoints. oauth_provisioning_available = auth_mode == "oauth" or (
oidc_config: dict[str, str] = {} mode == AuthMode.MULTI_USER_BASIC and settings.enable_offline_access
)
if oauth_provisioning_available:
# Provide IdP discovery information for NC PHP app
oidc_config = {}
if settings.oidc_discovery_url: if settings.oidc_discovery_url:
# Explicit OIDC_DISCOVERY_URL takes precedence oidc_config["discovery_url"] = settings.oidc_discovery_url
oidc_config["discovery_url"] = settings.oidc_discovery_url
elif settings.nextcloud_host:
# Auto-derive from NEXTCLOUD_HOST — Nextcloud exposes OIDC discovery
# at the standard well-known path when user_oidc is enabled
host = settings.nextcloud_host.rstrip("/")
oidc_config["discovery_url"] = f"{host}/.well-known/openid-configuration"
if settings.oidc_issuer: if settings.oidc_issuer:
oidc_config["issuer"] = settings.oidc_issuer oidc_config["issuer"] = settings.oidc_issuer
if oidc_config: if oidc_config:
response_data["oidc"] = oidc_config response_data["oidc"] = oidc_config
return JSONResponse(response_data) return JSONResponse(response_data)
+1 -129
View File
@@ -37,7 +37,6 @@ def create_mock_settings(
oidc_issuer: str | None = None, oidc_issuer: str | None = None,
vector_sync_enabled: bool = False, vector_sync_enabled: bool = False,
nextcloud_url: str = "http://localhost", nextcloud_url: str = "http://localhost",
nextcloud_host: str | None = "http://localhost",
enable_token_exchange: bool = False, enable_token_exchange: bool = False,
mcp_client_id: str | None = None, mcp_client_id: str | None = None,
mcp_client_secret: str | None = None, mcp_client_secret: str | None = None,
@@ -50,7 +49,6 @@ def create_mock_settings(
settings.oidc_issuer = oidc_issuer settings.oidc_issuer = oidc_issuer
settings.vector_sync_enabled = vector_sync_enabled settings.vector_sync_enabled = vector_sync_enabled
settings.nextcloud_url = nextcloud_url settings.nextcloud_url = nextcloud_url
settings.nextcloud_host = nextcloud_host
settings.enable_token_exchange = enable_token_exchange settings.enable_token_exchange = enable_token_exchange
settings.mcp_client_id = mcp_client_id settings.mcp_client_id = mcp_client_id
settings.mcp_client_secret = mcp_client_secret settings.mcp_client_secret = mcp_client_secret
@@ -135,7 +133,6 @@ class TestStatusEndpointOidcConfig:
enable_offline_access=False, # Key difference: no offline access enable_offline_access=False, # Key difference: no offline access
oidc_discovery_url="http://keycloak/.well-known/openid-configuration", oidc_discovery_url="http://keycloak/.well-known/openid-configuration",
oidc_issuer="http://keycloak/realms/test", oidc_issuer="http://keycloak/realms/test",
nextcloud_host=None,
) )
with ( with (
@@ -199,13 +196,12 @@ class TestStatusEndpointOidcConfig:
) )
def test_single_user_basic_no_oidc(self): def test_single_user_basic_no_oidc(self):
"""Test that single-user BasicAuth mode doesn't return OIDC config when no host.""" """Test that single-user BasicAuth mode doesn't return OIDC config."""
mock_settings = create_mock_settings( mock_settings = create_mock_settings(
enable_multi_user_basic=False, enable_multi_user_basic=False,
enable_offline_access=False, enable_offline_access=False,
oidc_discovery_url="http://keycloak/.well-known/openid-configuration", oidc_discovery_url="http://keycloak/.well-known/openid-configuration",
oidc_issuer="http://keycloak/realms/test", oidc_issuer="http://keycloak/realms/test",
nextcloud_host=None,
) )
with ( with (
@@ -348,127 +344,3 @@ class TestStatusEndpointBasicResponse:
data = response.json() data = response.json()
assert data["vector_sync_enabled"] is True assert data["vector_sync_enabled"] is True
class TestStatusEndpointOidcAutoDerivation:
"""Tests for OIDC discovery_url auto-derivation from NEXTCLOUD_HOST."""
def test_derives_discovery_url_from_nextcloud_host(self):
"""Test that discovery_url is auto-derived from nextcloud_url when not explicit."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://cloud.example.com/.well-known/openid-configuration"
)
def test_derives_discovery_url_strips_trailing_slash(self):
"""Test that trailing slash on nextcloud_host is stripped."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com/"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://cloud.example.com/.well-known/openid-configuration"
)
def test_explicit_discovery_url_takes_precedence(self):
"""Test that explicit OIDC_DISCOVERY_URL overrides auto-derivation."""
mock_settings = create_mock_settings(
oidc_discovery_url="https://keycloak.example.com/.well-known/openid-configuration",
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://keycloak.example.com/.well-known/openid-configuration"
)
def test_no_oidc_when_no_host_and_no_discovery_url(self):
"""Test that oidc block is absent when neither host nor discovery_url is set."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = None
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" not in data