mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 21:28:25 +08:00
chore: compatiable result is none
This commit is contained in:
parent
35696c6b2e
commit
212252bb78
@ -6,7 +6,7 @@ from typing import Any
|
||||
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import AliasChoices, BaseModel, ConfigDict, Field, ValidationError
|
||||
from pydantic import AliasChoices, BaseModel, ConfigDict, Field, ValidationError, field_validator
|
||||
from werkzeug.exceptions import Forbidden, NotFound
|
||||
|
||||
from configs import dify_config
|
||||
@ -246,6 +246,13 @@ class _ReplaceBindingsRequest(BaseModel):
|
||||
role_ids: list[str] = []
|
||||
account_ids: list[str] = []
|
||||
|
||||
@field_validator("role_ids", "account_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_bindings(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
@console_ns.route("/workspaces/current/rbac/my-permissions")
|
||||
class RBACMyPermissionsApi(Resource):
|
||||
@ -466,6 +473,13 @@ class RBACWorkspaceDatasetMemberBindingsApi(Resource):
|
||||
class _ReplaceMemberRolesRequest(BaseModel):
|
||||
role_ids: list[str] = []
|
||||
|
||||
@field_validator("role_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_role_ids(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
@console_ns.route("/workspaces/current/rbac/members/<uuid:member_id>/rbac-roles")
|
||||
class RBACMemberRolesApi(Resource):
|
||||
|
||||
@ -113,6 +113,13 @@ class AccessMatrixItem(_RBACModel):
|
||||
role_ids: list[str] = Field(default_factory=list)
|
||||
account_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("role_ids", "account_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_empty_lists(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
class AppAccessMatrix(_RBACModel):
|
||||
app_id: str = ""
|
||||
@ -194,15 +201,36 @@ class AccessPolicyUpdate(_RBACModel):
|
||||
class ReplaceRoleBindings(_RBACModel):
|
||||
role_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("role_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_role_ids(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
class ReplaceMemberBindings(_RBACModel):
|
||||
account_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("account_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_account_ids(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
class ReplaceBindings(_RBACModel):
|
||||
role_ids: list[str] = Field(default_factory=list)
|
||||
account_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("role_ids", "account_ids", mode="before")
|
||||
@classmethod
|
||||
def _coerce_bindings(cls, value: Any) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
return value
|
||||
|
||||
|
||||
class ListOption(_RBACModel):
|
||||
page_number: int | None = None
|
||||
|
||||
@ -2,11 +2,8 @@
|
||||
|
||||
The controllers here are thin: almost every non-trivial behaviour lives in
|
||||
``services.enterprise.rbac_service`` (covered by its own suite). These tests
|
||||
therefore focus on the three Flask-layer concerns the service layer cannot
|
||||
exercise:
|
||||
therefore focus on the Flask-layer concerns the service layer cannot exercise:
|
||||
|
||||
* ``enterprise_only`` rejects community-edition calls with 403 (it is the
|
||||
outermost decorator, so it fires before any auth middleware).
|
||||
* ``_current_ids`` raises 404 when the session has no tenant.
|
||||
* The pydantic request models accept / reject bodies as expected.
|
||||
|
||||
@ -25,7 +22,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from pydantic import ValidationError
|
||||
from werkzeug.exceptions import Forbidden, NotFound
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.console.workspace import rbac as rbac_mod
|
||||
|
||||
@ -40,26 +37,6 @@ def app():
|
||||
def _enabled(enabled: bool):
|
||||
return patch("controllers.console.workspace.rbac.dify_config.ENTERPRISE_ENABLED", enabled)
|
||||
|
||||
|
||||
class TestEnterpriseGate:
|
||||
"""``enterprise_only`` is the outermost decorator on every resource, so we
|
||||
can exercise it directly — no auth stubs required.
|
||||
"""
|
||||
|
||||
def test_catalog_forbidden_when_disabled(self, app):
|
||||
with app.test_request_context("/workspaces/current/rbac/role-permissions/catalog"), _enabled(False):
|
||||
with pytest.raises(Forbidden):
|
||||
rbac_mod.RBACWorkspaceCatalogApi().get()
|
||||
|
||||
def test_roles_post_forbidden_when_disabled(self, app):
|
||||
with (
|
||||
app.test_request_context("/workspaces/current/rbac/roles", method="POST", json={}),
|
||||
_enabled(False),
|
||||
):
|
||||
with pytest.raises(Forbidden):
|
||||
rbac_mod.RBACRolesApi().post()
|
||||
|
||||
|
||||
class TestCurrentIds:
|
||||
def test_rejects_missing_tenant(self):
|
||||
with patch("controllers.console.workspace.rbac.current_account_with_tenant") as mock_user:
|
||||
@ -115,6 +92,15 @@ class TestPydanticModels:
|
||||
assert parsed.role_ids == []
|
||||
assert parsed.account_ids == []
|
||||
|
||||
def test_replace_bindings_coerce_null_lists(self):
|
||||
parsed = rbac_mod._ReplaceBindingsRequest.model_validate({"role_ids": None, "account_ids": None})
|
||||
assert parsed.role_ids == []
|
||||
assert parsed.account_ids == []
|
||||
|
||||
def test_replace_member_roles_coerce_null_list(self):
|
||||
parsed = rbac_mod._ReplaceMemberRolesRequest.model_validate({"role_ids": None})
|
||||
assert parsed.role_ids == []
|
||||
|
||||
def test_pagination_query_accepts_page_and_limit_aliases(self):
|
||||
parsed = rbac_mod._PaginationQuery.model_validate({"page": 3, "limit": 25, "reverse": True})
|
||||
assert parsed.page_number == 3
|
||||
|
||||
@ -252,6 +252,27 @@ class TestWorkspaceAccess:
|
||||
assert call.endpoint == "/rbac/workspace/datasets/access-policy"
|
||||
assert call.params is None
|
||||
|
||||
def test_workspace_matrix_coerces_null_bindings(self, mock_send: MagicMock):
|
||||
mock_send.return_value = {
|
||||
"items": [
|
||||
{
|
||||
"policy": {
|
||||
"id": "policy-1",
|
||||
"resource_type": "app",
|
||||
"name": "Workspace App Access",
|
||||
},
|
||||
"role_ids": None,
|
||||
"account_ids": None,
|
||||
}
|
||||
],
|
||||
"pagination": None,
|
||||
}
|
||||
|
||||
out = svc.RBACService.WorkspaceAccess.app_matrix("tenant-1")
|
||||
|
||||
assert out.items[0].role_ids == []
|
||||
assert out.items[0].account_ids == []
|
||||
|
||||
def test_workspace_app_replace_bindings(self, mock_send: MagicMock):
|
||||
mock_send.return_value = {"data": []}
|
||||
payload = svc.ReplaceBindings(role_ids=["workspace.editor"], account_ids=["acct-2"])
|
||||
|
||||
Loading…
Reference in New Issue
Block a user