Repository navigation
refactor: add knowledge-base tokenize permission bit - #7759
Conversation
|
|
||
| # ==================== 资源管理 - 工具 ==================== | ||
| RESOURCE_TOOL_READ = ( | ||
| Permission(group=Group.SYSTEM_RES_TOOL, sub_group=Group.SYSTEM_RES_TOOL, operate=Operate.READ, bit_index=0), |
There was a problem hiding this comment.
Based on the diff, there are no obvious syntax errors, but there are several things to verify:
-
bit_indexuniqueness- Verify
bit_index=73is not already used withinGroup.KNOWLEDGE / Group.KNOWLEDGE. SHARED_KNOWLEDGE_TOKENandRESOURCE_KNOWLEDGE_TOKENboth usebit_index=65. This is fine only ifbit_indexis scoped per(group, sub_group). If bit indexes are global or mixed across groups, this is a collision.- Confirm
bit_index=65is not already used insideGroup.SYSTEM_SHARED_KNOWLEDGEorGroup.SYSTEM_RES_KNOWLEDGE.
- Verify
-
64-bit overflow risk
bit_index=65andbit_index=73exceed 63. If permission bitmasks are stored as 64-bit integers,BIGINT, or JavaScript numbers, these bits may overflow or lose precision.- Ensure the storage/serialization layer supports arbitrary-size integers, byte arrays, strings, or a sufficiently large bitmask type.
-
Security / least privilege for
KNOWLEDGE_TOKENKNOWLEDGE_TOKENis granted toWORKSPACE_MANAGEandUSER, with scopesWORKSPACEandWORKSPACE_RESOURCE.- Confirm that normal users should be allowed to trigger knowledge tokenization/indexing. If this is an administrative operation, restrict it to
ADMINor management roles.
-
EE / non-EE consistency
SHARED_KNOWLEDGE_TOKENandRESOURCE_KNOWLEDGE_TOKENuseis_ee=is_ee, whileKNOWLEDGE_TOKENdoes not.- Confirm this is intentional and matches the existing pattern for workspace-level permissions.
-
Operate.TOKENintegration- Ensure
Operate.TOKENis defined and handled consistently in:- permission serializers/deserializers
- API authorization decorators
- frontend permission mappings
- audit logging
- any permission migration or cache invalidation logic
- Ensure
-
Naming / comments
TOKENis somewhat ambiguous. If it means “tokenization/indexing,” considerTOKENIZEorINDEX_TOKEN, or add a comment.- Add a comment for
KNOWLEDGE_TOKENandSHARED_KNOWLEDGE_TOKENsimilar to the existing# 资源管理 - 知识库分词索引comment.
-
resource_permission_group_listconsistencyKNOWLEDGE_TOKENincludesresource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANAGE].- Confirm that shared/resource token permissions intentionally do not need a similar mapping.
Overall: the main risks are bit-index collisions and >64-bit permission storage. The rest are consistency and least-privilege checks.
|
|
||
| @patch("knowledge.views.knowledge.KnowledgeSerializer.Operate") | ||
| def test_detail_redaction_and_settings_section_permissions(self, serializer): | ||
| serializer.return_value.one.side_effect = lambda: { |
There was a problem hiding this comment.
Here's my review of the added test code:
Issues & Potential Problems
1. Incorrect patch signature / argument order (critical)
@patch("common.log.log.Log")
@patch("knowledge.views.knowledge.KnowledgeSerializer.Operate")
def test_...(self, serializer, log):Decorators apply bottom-up, so the innermost decorator is the last argument. That means serializer would actually receive the Log mock and log would receive Operate. The parameter names map in reverse order to what you likely intend.
- Bottom decorator (
Operate) → first arg afterself=serializer✅ actually correct - Top decorator (
Log) → second arg =log✅
So the order happens to be correct here, but it's fragile. Since you're asserting only on serializer, consider not patching Log at all, or naming parameters explicitly as mock_operate, mock_log for clarity.
2. KnowledgeView.Tokenize() is instantiated fresh each call — verify this is intended
You create a new view instance three times (Tokenize().put(...)). If Tokenize holds any state across calls, or if assert_not_called() relies on the same serializer mock being reset, confirm the mock is shared correctly. You do reset_mock() inside the loop, which is fine, but note that you never reset before the first negative-case assertion — you rely on reset_mock() being called at the top of each iteration. This works but is easy to misread. Suggest resetting once before the loop:
serializer.reset_mock()
for permission in (...):
...3. Missing assertion on the happy path response
On the successful call you only assert serializer.assert_called_once(). Consider also asserting that tokenize was actually invoked with expected args (e.g., serializer.return_value.tokenize.assert_called_once()), otherwise the test doesn't prove the correct method ran.
4. No assertion that the exception type/message is specific
with self.assertRaises(AppUnauthorizedFailed): is good, but consider asserting it raises specifically due to permission checks (e.g., message content), so the test won't pass for the wrong reason.
5. Magic permissions list not parametrized
Using a for loop inside one test means a failure reports the whole test, not which permission failed. Use subTest:
for permission in (...):
with self.subTest(permission=permission):
...Or split into separate parametrized tests.
6. P.KNOWLEDGE_EDIT grouping is slightly odd
Grouping document-token, image-token, and edit together implies they all fail the tokenize permission check. Make sure that's intentional — if KNOWLEDGE_EDIT should be allowed, this test will silently enforce wrong behavior.
7. Potential missing reset of side effects
serializer.return_value.tokenize.return_value = {} is set once; after reset_mock(), return values persist (reset_mock only clears calls, not return_values by default in older mocks). Fine here, but be aware.
Optimization Suggestions
- Remove the unused
Logpatch unless required by the code under test to suppress logging. - Rename params to
mock_operate, mock_logfor readability. - Add
subTestper permission. - Add an explicit assertion that
tokenizewas called (and possibly with expected kwargs). - Optionally assert the success path returns the expected JSON payload.
Summary
Logic is broadly correct, but the test is brittle due to decorator/argument ordering conventions, silent multi-permission failures, and weak assertions on the positive path. Refactoring lightly will improve reliability and diagnostics.
| PermissionConstants.KNOWLEDGE_TOKEN.get_workspace_permission_workspace_manage_role(), | ||
| RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), | ||
| ViewPermission( | ||
| [RoleConstants.USER.get_workspace_role()], |
There was a problem hiding this comment.
Review of the diff
The change replaces KNOWLEDGE_DOCUMENT_TOKEN with KNOWLEDGE_TOKEN in two permission lookups. Observations:
-
Verify
KNOWLEDGE_TOKENactually exists- Confirm that
PermissionConstants.KNOWLEDGE_TOKENis defined and that both helper methods (get_workspace_knowledge_permission()andget_workspace_permission_workspace_manage_role()) are implemented on it. If it was only recently added, ensure no environment/branch has an older constant set.
- Confirm that
-
Semantic scope of the permission changed
- This endpoint class is
Tokenize. Moving from a document-token permission to a broader knowledge-token permission widens or shifts the authorization boundary. Double-check this matches the intended access model — e.g., whether users who could tokenize documents before but not general knowledge tokens (or vice versa) will see a behavior change. Add a regression/permission test for the new constant.
- This endpoint class is
-
Consistency across the codebase
- Grep for other usages of
KNOWLEDGE_DOCUMENT_TOKEN. If some remain while this one moved toKNOWLEDGE_TOKEN, you may end up with inconsistent permissions between related endpoints (e.g., upload vs. tokenize). Consider migrating all related call sites in one change.
- Grep for other usages of
-
Long line / readability (minor)
- The lines exceed typical 88–100 chars. Not a bug, but consider wrapping:
either via a local alias or a shorter variable to keep within linters' max-line-length.
PermissionConstants.KNOWLEDGE_TOKEN.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_TOKEN.get_workspace_permission_workspace_manage_role(),
- The lines exceed typical 88–100 chars. Not a bug, but consider wrapping:
-
Duplicated calls (minor optimization)
- Two method calls on the same constant resolve two permissions; if these getters are non-trivial (DB/lookup based), compute once into locals and reuse. Low priority if they're pure static returns.
-
Missing type ignore comment
- The decorator still has
# type: ignoreon the cartesian/extend_schemaabove; if the type checker flagged the old constant, re-run mypy/pyright to confirm the new name doesn't produce a different diagnostic, and adjust annotations if needed.
- The decorator still has
-
No behavioral test in the diff
- Recommend adding/updating a unit test asserting that a user with only
KNOWLEDGE_DOCUMENT_TOKENpermission is now denied/allowed as intended, to lock in the authorization change.
- Recommend adding/updating a unit test asserting that a user with only
Overall: the edit itself is syntactically fine, but verify the new constant’s existence, keep permission definitions consistent across related endpoints, and add a permission test to guard the changed authorization semantics.
refactor: add knowledge-base tokenize permission bit