Skip to content

fix(rbac): enforce access-control permissions - #40902

Open
WH-2099 wants to merge 8 commits into
mainfrom
fix/37988-protect-rbac-control-plane
Open

fix(rbac): enforce access-control permissions#40902
WH-2099 wants to merge 8 commits into
mainfrom
fix/37988-protect-rbac-control-plane

Conversation

@WH-2099

@WH-2099 WH-2099 commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • Protect all 16 app and dataset access-config methods with their existing resource-scoped RBAC permissions and legacy gates.
  • Protect all 8 workspace app and dataset access-rule methods, member-role updates, and role-member listing with workspace role-management permission.
  • Validate target workspace membership before access or role mutations and scrub tenant-external identities from RBAC responses.
  • Preserve canonical legacy member invariants and reject RBAC self-assignment, owner reassignment, foreign members, duplicate roles, and non-canonical role identifiers.
  • Move resource resolution and member mutation database work into short owned sessions so RBAC HTTP calls never inherit request transactions.
  • Harden invitation and activation flows with bounded locks, authoritative seat and member-capacity checks, trusted role propagation, idempotent activation, and failure cleanup.
  • Keep member removal on workspace.member.manage while role assignment requires workspace.role.manage, including the matching frontend capability split.
  • Fail RBAC owner transfer with HTTP 503 until an atomic enterprise inner endpoint exists instead of attempting an unsafe multi-call ownership saga.
  • Update the generated Console OpenAPI document for the narrowed user-policy payload.
  • Cover the “Per-resource RBAC access config”, “RBAC member role assignment”, and “Workspace-level app and dataset access rules” checklist items in enhancement: resource-level permission consistency #37988.
  • Part of enhancement: resource-level permission consistency #37988.
  • Part of enhancement: track resource boundary consistency improvements #37983.

Screenshots

Not applicable.

Validation

  • Targeted backend unit, controller, service, task, Swagger, and schema tests: 399 passed.
  • Container integration suites: 110 tests collected successfully; execution remains CI-owned.
  • Focused frontend tests: 35 passed.
  • Ruff check passed for all 20 changed Python files.
  • Ruff format check passed for all 20 changed Python files.
  • Pyrefly reported 0 diagnostics for all 10 changed backend production files.
  • Git diff checks passed.

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues.
  • I have added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I have updated the generated API documentation accordingly.
  • I ran targeted backend lint, format, type, and test checks plus focused frontend tests.

From Codex.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 17, 2026
@WH-2099 WH-2099 self-assigned this Aug 17, 2026
@github-actions github-actions Bot added the web This relates to changes on the web. label Aug 17, 2026
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 59.35% 59.36% +0.01%
Strict coverage 58.93% 58.94% +0.01%
Typed symbols 39,611 39,685 +74
Untyped symbols 27,323 27,361 +38
Modules 3174 3174 0

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-08-17 23:13:20.423509326 +0000
+++ /tmp/pyrefly_pr.txt	2026-08-17 23:13:06.721431022 +0000
@@ -795,21 +795,21 @@
 ERROR Argument `list[str]` is not assignable to parameter `roles` with type `list[TenantAccountRole]` in function `services.account_service.TenantService.has_roles` [bad-argument-type]
     --> tests/test_containers_integration_tests/services/test_account_service.py:1696:45
 ERROR Object of class `NoneType` has no attribute `role` [missing-attribute]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2052:16
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2046:16
 ERROR Object of class `NoneType` has no attribute `role` [missing-attribute]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2108:16
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2106:16
 ERROR Object of class `NoneType` has no attribute `role` [missing-attribute]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2109:16
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2107:16
 ERROR Object of class `NoneType` has no attribute `id` [missing-attribute]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2255:16
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2251:16
 ERROR Argument `dict[str, str | dict[str, bool]]` is not assignable to parameter `value` with type `TenantCustomConfigDict` in function `models.account.Tenant.custom_config_dict` [bad-argument-type]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2425:37
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2421:37
 ERROR TypedDict `TenantCustomConfigDict` does not have key `theme` [bad-typed-dict-key]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2433:33
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2429:33
 ERROR TypedDict `TenantCustomConfigDict` does not have key `language` [bad-typed-dict-key]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2434:33
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2430:33
 ERROR TypedDict `TenantCustomConfigDict` does not have key `feature_flags` [bad-typed-dict-key]
-    --> tests/test_containers_integration_tests/services/test_account_service.py:2435:33
+    --> tests/test_containers_integration_tests/services/test_account_service.py:2431:33
 ERROR Argument `dict[str, AppMode | str] | dict[str, str]` is not assignable to parameter `args` with type `AdvancedPromptTemplateArgs` in function `services.advanced_prompt_template_service.AdvancedPromptTemplateService.get_prompt` [bad-argument-type]
    --> tests/test_containers_integration_tests/services/test_advanced_prompt_template_service.py:718:63
 ERROR Argument `dict[str, AppMode | str]` is not assignable to parameter `args` with type `AdvancedPromptTemplateArgs` in function `services.advanced_prompt_template_service.AdvancedPromptTemplateService.get_prompt` [bad-argument-type]
@@ -3431,7 +3431,7 @@
 ERROR Cannot set item in `OrderedDict[str, bool | list[str] | str]` [unsupported-operation]
    --> tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py:137:47
 ERROR Argument `() -> Session` is not assignable to parameter `session_factory` with type `sessionmaker[@_]` in function `sqlalchemy.orm.scoping.scoped_session.__init__` [bad-argument-type]
-  --> tests/unit_tests/controllers/console/auth/test_account_activation.py:23:36
+  --> tests/unit_tests/controllers/console/auth/test_account_activation.py:26:36
 ERROR Object of class `FromClause` has no attribute `create` [missing-attribute]
   --> tests/unit_tests/controllers/console/auth/test_forgot_password.py:29:9
 ERROR `None` is not subscriptable [unsupported-operation]
@@ -3529,19 +3529,19 @@
 ERROR Object of class `Flask` has no attribute `login_manager` [missing-attribute]
   --> tests/unit_tests/controllers/console/test_workspace_members.py:18:5
 ERROR `SimpleNamespace` is not assignable to attribute `_current_tenant` with type `Tenant | None` [bad-assignment]
-  --> tests/unit_tests/controllers/console/test_workspace_members.py:65:43
+  --> tests/unit_tests/controllers/console/test_workspace_members.py:64:43
 ERROR `SimpleNamespace` is not assignable to attribute `_current_tenant` with type `Tenant | None` [bad-assignment]
-   --> tests/unit_tests/controllers/console/test_workspace_members.py:109:43
+   --> tests/unit_tests/controllers/console/test_workspace_members.py:108:43
 ERROR `SimpleNamespace` is not assignable to attribute `_current_tenant` with type `Tenant | None` [bad-assignment]
-   --> tests/unit_tests/controllers/console/test_workspace_members.py:143:43
+   --> tests/unit_tests/controllers/console/test_workspace_members.py:142:43
 ERROR `SimpleNamespace` is not assignable to attribute `_current_tenant` with type `Tenant | None` [bad-assignment]
-   --> tests/unit_tests/controllers/console/test_workspace_members.py:176:43
+   --> tests/unit_tests/controllers/console/test_workspace_members.py:175:43
 ERROR Type `Response` is not iterable [not-iterable]
    --> tests/unit_tests/controllers/console/test_wraps.py:190:17
 ERROR Argument `Literal['app']` is not assignable to parameter `resource_type` with type `RBACResourceScope` in function `controllers.common.wraps._extract_resource_id` [bad-argument-type]
-   --> tests/unit_tests/controllers/console/test_wraps.py:378:41
+   --> tests/unit_tests/controllers/console/test_wraps.py:398:41
 ERROR Argument `Literal['app']` is not assignable to parameter `resource_type` with type `RBACResourceScope` in function `controllers.common.wraps._extract_resource_id` [bad-argument-type]
-   --> tests/unit_tests/controllers/console/test_wraps.py:386:41
+   --> tests/unit_tests/controllers/console/test_wraps.py:406:41
 ERROR Argument value `Literal[0]` violates Pydantic `ge` constraint `Literal[1]` for field `page` [bad-argument-type]
    --> tests/unit_tests/controllers/console/workspace/test_endpoint.py:185:47
 ERROR Missing argument `plugin_id` in function `controllers.console.workspace.endpoint.EndpointListForPluginQuery.__init__` [missing-argument]
@@ -8578,6 +8578,28 @@
   --> tests/unit_tests/services/enterprise/test_plugin_manager_service.py:59:25
 ERROR Argument `None` is not assignable to parameter `response` with type `Response` in function `httpx._exceptions.HTTPStatusError.__init__` [bad-argument-type]
   --> tests/unit_tests/services/enterprise/test_plugin_manager_service.py:60:26
+ERROR Argument `Literal['admin']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:36:79
+ERROR Argument `Literal['admin']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:37:83
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:38:79
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:39:84
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:55:81
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+  --> tests/unit_tests/services/enterprise/test_rbac_service.py:56:81
+ERROR Argument `Literal['admin']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+   --> tests/unit_tests/services/enterprise/test_rbac_service.py:829:83
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+   --> tests/unit_tests/services/enterprise/test_rbac_service.py:830:83
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+    --> tests/unit_tests/services/enterprise/test_rbac_service.py:1022:100
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+    --> tests/unit_tests/services/enterprise/test_rbac_service.py:1051:100
+ERROR Argument `Literal['normal']` is not assignable to parameter `role` with type `SQLCoreOperations[TenantAccountRole] | TenantAccountRole` in function `models.account.TenantAccountJoin.__init__` [bad-argument-type]
+    --> tests/unit_tests/services/enterprise/test_rbac_service.py:1097:94
 ERROR Cannot index into `list[Unknown]` [bad-index]
    --> tests/unit_tests/services/hit_service.py:434:20
 ERROR Cannot index into `object` [bad-index]

@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.86207% with 161 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.78%. Comparing base (02f0e8c) to head (1070931).

Files with missing lines Patch % Lines
api/controllers/console/workspace/rbac.py 60.81% 57 Missing and 1 partial ⚠️
api/services/account_service.py 76.63% 37 Missing and 20 partials ⚠️
api/services/enterprise/rbac_service.py 82.44% 16 Missing and 7 partials ⚠️
api/controllers/console/workspace/members.py 83.82% 5 Missing and 6 partials ⚠️
api/controllers/console/auth/activate.py 78.37% 5 Missing and 3 partials ⚠️
api/controllers/openapi/workspaces.py 80.00% 3 Missing ⚠️
api/controllers/common/wraps.py 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #40902      +/-   ##
==========================================
+ Coverage   86.76%   86.78%   +0.02%     
==========================================
  Files        5205     5362     +157     
  Lines      292600   297007    +4407     
  Branches    58202    59346    +1144     
==========================================
+ Hits       253881   257769    +3888     
- Misses      33717    34224     +507     
- Partials     5002     5014      +12     
Flag Coverage Δ
api 86.76% <75.45%> (-0.03%) ⬇️
cli 89.33% <ø> (?)
dify-ui 93.45% <ø> (ø)
web 86.68% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 17, 2026
@autofix-ci
autofix-ci Bot requested a review from crazywoola as a code owner August 17, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files. web This relates to changes on the web.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant