fix: [2.6] enforce partition statistics privilege (#52248)
Project: milvus-io/milvus | Language: Go | Concepts: security, testing-strategy | Difficulty: mid
Link: https://github.com/milvus-io/milvus/pull/52466
The problem
The system did not enforce privilege checks for partition-level statistics requests. This meant users could retrieve statistics for a collection's partitions even if their role lacked the explicit GetStatistics privilege, violating Role-Based Access Control (RBAC) expectations and creating a security gap.
Why it's non-trivial
A naive fix might only update privilege checks for collection-level statistics, missing the parallel need for partition-level checks. Furthermore, the privilege enforcement logic must be consistent with how privileges are defined in the protocol buffers and enforced throughout the codebase. Proper testing is also needed to ensure both allowed and denied cases are handled as expected.
The approach
1. Update dependencies to ensure proto definitions are correct:
The PR bumps the milvus-proto/go-api/v2 dependency to a pseudo-version that includes correct privilege annotations for statistics requests:
- github.com/milvus-io/milvus-proto/go-api/v2 v2.6.22
+ github.com/milvus-io/milvus-proto/go-api/v2 v2.6.23-0.20260806082413-a76db6514428
This ensures the privilege requirements for GetCollectionStatisticsRequest and GetPartitionStatisticsRequest are up-to-date and enforced.
2. Add targeted privilege enforcement tests:
A new test, TestPrivilegeInterceptorStatisticsRBAC, is introduced to verify RBAC enforcement for both collection and partition statistics:
func TestPrivilegeInterceptorStatisticsRBAC(t *testing.T) {
...
t.Run("authenticated principal without GetStatistics is denied", func(t *testing.T) {
initPolicy([]string{
funcutil.PolicyForPrivilege(roleName, commonpb.ObjectType_Collection.String(), collectionName, commonpb.ObjectPrivilege_PrivilegeLoad.String(), util.DefaultDBName),
})
assertDenied(t, collectionReq())
assertDenied(t, partitionReq())
})
t.Run("GetStatistics grant allows statistics requests", func(t *testing.T) {
initPolicy([]string{
funcutil.PolicyForPrivilege(roleName, commonpb.ObjectType_Collection.String(), collectionName, commonpb.ObjectPrivilege_PrivilegeGetStatistics.String(), util.DefaultDBName),
})
assertAllowed(t, collectionReq())
assertAllowed(t, partitionReq())
})
}
This test verifies that users without the GetStatistics privilege are denied access, while those with it are allowed, for both collection and partition statistics requests.
3. Ensure test coverage and cache invalidation:
The test setup includes privilege cache cleanup and parameter resets to avoid side effects:
defer Params.Reset(Params.CommonCfg.AuthorizationEnabled.Key)
defer privilege.CleanPrivilegeCache()
This prevents test pollution and ensures reliable, repeatable test outcomes.
From the review thread
No notable review discussion on this PR.
Takeaways
- Always enforce privilege checks at every relevant API granularity (e.g., both collection and partition), not just the most obvious or highest level.
- When updating privilege logic, ensure protocol definitions and code dependencies are in sync so that privilege annotations are actually enforced.
- Automated tests should explicitly cover both allowed and denied cases for privilege checks to prevent security regressions.