fix(openapi): refuse web-app access_mode public when the server disables public access

This commit is contained in:
GareArc
2026-10-13 00:37:42 -07:00
parent b9addab226
commit 5c84ac3440
3 changed files with 63 additions and 2 deletions
+7 -1
View File
@@ -12,7 +12,11 @@ from pydantic import BaseModel
from werkzeug.exceptions import NotFound
from configs import dify_config
from controllers.openapi._errors import WebAppAccessModeConsoleOnly, WebAppAccessUnavailable
from controllers.openapi._errors import (
WebAppAccessModeConsoleOnly,
WebAppAccessUnavailable,
WebAppPublicAccessDisabled,
)
from controllers.openapi._models import (
AgentAppInfo,
AgentAppInfoPatch,
@@ -212,6 +216,8 @@ def webapp_access(ctx: Context) -> WebAppAccess:
def update_webapp_access(ctx: Context, body: WebAppAccessPayload) -> WebAppAccess:
if body.access_mode not in _SETTABLE_ACCESS_MODES:
raise WebAppAccessModeConsoleOnly()
if body.access_mode == WebAppAccessMode.PUBLIC and not dify_config.WEBAPP_PUBLIC_ACCESS_ENABLED:
raise WebAppPublicAccessDisabled()
try:
_ACCESS_POLICY.update_access_mode(ctx.app.id, body.access_mode)
except WebAppAccessUnavailableError as error:
+8
View File
@@ -83,6 +83,7 @@ class OpenApiErrorCode(StrEnum):
NODE_TYPE_NOT_FOUND = "node_type_not_found"
NODE_NOT_FOUND = "node_not_found"
WEBAPP_ACCESS_MODE_CONSOLE_ONLY = "webapp_access_mode_console_only"
WEBAPP_PUBLIC_ACCESS_DISABLED = "webapp_public_access_disabled"
SECRET_MASK_NOT_SECRET = "secret_mask_not_secret"
MARKETPLACE_DISABLED = "marketplace_disabled"
MARKETPLACE_UNAVAILABLE = "marketplace_unavailable"
@@ -351,6 +352,13 @@ class WebAppAccessModeConsoleOnly(OpenApiError): # noqa: N818
hint = "Use public, private_all or sso_verified here."
class WebAppPublicAccessDisabled(OpenApiError): # noqa: N818
code = 403
error_code = OpenApiErrorCode.WEBAPP_PUBLIC_ACCESS_DISABLED
description = "This server does not allow web apps with access_mode public."
hint = "Use private_all or sso_verified."
class VersionNotRestorable(OpenApiError): # noqa: N818
code = 400
error_code = OpenApiErrorCode.VERSION_NOT_RESTORABLE
@@ -6,7 +6,7 @@ import pytest
from controllers.openapi import _app_settings
from controllers.openapi._app_settings import WebAppPath
from controllers.openapi._errors import WebAppAccessUnavailable
from controllers.openapi._errors import WebAppAccessModeConsoleOnly, WebAppAccessUnavailable, WebAppPublicAccessDisabled
from controllers.openapi._models import WebAppAccessPayload
from controllers.openapi.auth.context import Context
from enums import WebAppAccessMode
@@ -43,6 +43,53 @@ def test_set_webapp_access_returns_the_written_mode_without_reading_back(monkeyp
policy.update_access_mode.assert_called_once_with("app-1", WebAppAccessMode.PRIVATE_ALL)
def test_set_webapp_access_public_is_refused_when_the_server_disables_public_access(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(_app_settings.dify_config, "WEBAPP_PUBLIC_ACCESS_ENABLED", False)
policy = Mock()
monkeypatch.setattr(_app_settings, "_ACCESS_POLICY", policy)
with pytest.raises(WebAppPublicAccessDisabled):
_app_settings.update_webapp_access(_context(), WebAppAccessPayload(access_mode=WebAppAccessMode.PUBLIC))
policy.update_access_mode.assert_not_called()
@pytest.mark.parametrize("mode", [WebAppAccessMode.PRIVATE_ALL, WebAppAccessMode.SSO_VERIFIED])
def test_set_webapp_access_non_public_modes_are_unaffected_by_the_public_switch(
monkeypatch: pytest.MonkeyPatch, mode: WebAppAccessMode
) -> None:
monkeypatch.setattr(_app_settings.dify_config, "WEBAPP_PUBLIC_ACCESS_ENABLED", False)
policy = Mock()
monkeypatch.setattr(_app_settings, "_ACCESS_POLICY", policy)
result = _app_settings.update_webapp_access(_context(), WebAppAccessPayload(access_mode=mode))
assert result.access_mode == mode
policy.update_access_mode.assert_called_once_with("app-1", mode)
def test_set_webapp_access_public_is_written_when_the_server_allows_it(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_app_settings.dify_config, "WEBAPP_PUBLIC_ACCESS_ENABLED", True)
policy = Mock()
monkeypatch.setattr(_app_settings, "_ACCESS_POLICY", policy)
result = _app_settings.update_webapp_access(_context(), WebAppAccessPayload(access_mode=WebAppAccessMode.PUBLIC))
assert result.access_mode == WebAppAccessMode.PUBLIC
policy.update_access_mode.assert_called_once_with("app-1", WebAppAccessMode.PUBLIC)
def test_set_webapp_access_private_still_answers_console_only_when_public_is_disabled(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(_app_settings.dify_config, "WEBAPP_PUBLIC_ACCESS_ENABLED", False)
with pytest.raises(WebAppAccessModeConsoleOnly):
_app_settings.update_webapp_access(_context(), WebAppAccessPayload(access_mode=WebAppAccessMode.PRIVATE))
@pytest.mark.parametrize(
("mode", "path"),
[