Fix checkRoleEscalation performance and bugs in access checking - #12973
Fix checkRoleEscalation performance and bugs in access checking#12973nvazquez wants to merge 16 commits into
Conversation
Replace per-API-command loop (~1,484 DB calls) with batch getApisAllowedToAccount calls using cached role permissions, reducing to ~2 DB calls (or 0 with warm cache). Fix NPE on pde.getAccount(), format string mismatch (3 %s but 4 args), and use cached permissions in DynamicRoleBasedAPIAccessChecker.checkAccess(Account, String).
…ccount Add tests for DynamicRoleBasedAPIAccessChecker covering checkAccess(Account, String) and getApisAllowedToAccount including cache verification, admin pass-through, allow/deny filtering, and annotation fallback. Add tests for AccountManagerImpl.checkRoleEscalation covering same permissions, caller superset, escalation detection, empty API list, and multi-checker chaining.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 4.22 #12973 +/- ##
============================================
+ Coverage 17.60% 17.70% +0.09%
- Complexity 15677 15855 +178
============================================
Files 5918 5926 +8
Lines 531681 533592 +1911
Branches 65005 65283 +278
============================================
+ Hits 93623 94490 +867
- Misses 427498 428421 +923
- Partials 10560 10681 +121
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@blueorangutan package |
|
@nvazquez a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✖️ debian ✔️ suse15. SL-JID 17387 |
|
@blueorangutan test |
|
@nvazquez a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
[SF] Trillian test result (tid-15819)
|
|
@blueorangutan package |
|
@DaanHoogland a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17416 |
|
I partially tested this PR while testing PR #13044 (which moves I reproduced the same case on a baseline build and got the exact same result, so the issue is pre-existing in
In both scenarios the request fails before anything is written to the database - the log shows the account only in memory (id:0), so no rows are inserted. That part works correctly. Suggestions:
Detailed test casesBuild under test: 4.22.1.0-SNAPSHOT with PR #12973 applied on Oracle Linux 9. Baseline reproductions confirmed on a mainline 4.22.1.0 build without this PR (escalation succeeds). Setup on the PR build
Scenario 1 - Default Domain Admin tries to create a User account with the Root Admin roleRun as Result: The escalation is blocked. No rows in But the error message is confusing. The user sees "The account has role null or unknown" with HTTP 531, which doesn't say anything about a privilege escalation. It sounds like a missing-role bug. What's happening: When a Domain Admin asks about the Root Admin role, On the baseline build, the same null-role exception happens, but the old code's Scenario 2 - Restricted custom Domain Admin tries to create a default Domain Admin account (issue #5781)Run as Result: Blocked, with a clear and helpful error message that names exactly which API was the problem ( The user sees |
|
Thanks @RosiKyu - have fixed the scenario 1 as per your comments, scenario 2 seems to be working fine @blueorangutan package |
|
@nvazquez a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18821 |
|
@blueorangutan test |
|
@nvazquez a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
[SF] Trillian test result (tid-16735)
|
|
@nvazquez, I have re-tested the PR on a 4.22 KVM environment, on a build that includes the latest fix commit, alongside a 4.22 build without the PR. Everything below is a measured before and after. The performance fix works as expected.
A single account creation took over 10 seconds on the build without the PR.
The first call reads the permissions once, and later calls reuse them from memory until the cache period runs out. Escalation is still correctly blocked, and the new The improved message for the missing role case works. A default Domain Admin creating a User account with the Root Admin role now gets: instead of the previous No regressions found. All 17 account and user APIs affected by this change were exercised as a Domain Admin against an account it owns: create, update, enable, disable and delete account; create, update, enable, disable and delete user; register and get user keys; move user; update resource limit and count. All pass. Valid account creations are not wrongly refused, including a restricted role creating an account that carries that same role. Two additional issues found1. Revoking a permission does not take effect until the cache expiresOnly happens when After a permission is revoked from a role, account creation carries on using the remembered copy. A restricted Domain Admin can create an account that still has the revoked permission, for as long as the cache period lasts. The window is temporary but the account created in it is permanent, so this reopens #5781 for that period. Steps to reproduce, with
Step 6 should be refused with Steps 3 to 6 have to be done within the cache period, counted from step 3, because step 3 is what puts the permission into memory. If that time runs out in between, the database is read again correctly and the problem does not show. Observed, 49 seconds after step 3, with the database reading The same sequence on a build without the PR refuses it immediately, with the same cache setting: Setting This works in both directions. A newly granted permission also fails to take effect until the cache period runs out. That is not a security problem, but it will be reported as permissions not working. Suggestion. Clear the remembered permissions for a role when they are changed, or read them from the database for this particular check. 2. A long list of escalated APIs makes the API return HTTP 500 with an empty responseThis one does not need the cache. It happens on a default installation. Every differing API name is put into the error message with no limit. Past a certain length the API stops returning the error at all. Tested by calling
The identical call on a build without the PR returns a proper error:
Steps to reproduce, no special configuration needed:
cmk shows the failing case as: So the user gets no error text at all, and a The escalation is still correctly refused and nothing is written to the database, so this is not a security issue. This also hides the improved message from 81df63e whenever the list is long, so that improvement is only visible when the list is short. Suggestion. Limit the list in the message, for example the first 10 followed by |
Description
This PR fixes performance issues and bugs in AccountManagerImpl.checkRoleEscalation(Account, Account).
Performance problem: The method iterated all ~742 API commands individually, calling checkApiAccess for each command through 2 API checkers. DynamicRoleBasedAPIAccessChecker.checkAccess(Account, String) made a redundant uncached DB call on every invocation, ignoring already-cached data from
getRolePermissionsUsingCache(). Additionally, ProjectRoleBasedApiAccessChecker.checkAccess(Account, String) always returns true but was still called 742 times.
Impact before fix: ~1,484 DB calls per checkRoleEscalation invocation for non-admin roles.
Impact after fix: ~2 DB calls (or 0 with warm cache).
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?