RosiKyu commented on PR #12973:
URL: https://github.com/apache/cloudstack/pull/12973#issuecomment-4330355036

   I partially tested this PR while testing PR #13044 (which moves 
`checkRoleEscalation` outside the DB transaction in `createAccount`). During 
that testing, I hit a case on the PR #13044 build where a default Domain Admin 
successfully created a User-type account and assigned it the Root Admin role. 
The escalation went through and the account was persisted in the DB with 
`role_id=1`.
   
   I reproduced the same case on a baseline build (no PR #13044) and got the 
exact same result, so the issue is pre-existing in `checkRoleEscalation` and 
not introduced by PR #13044. PR #12973 was already in flight to address 
`checkRoleEscalation`, so I built a third environment with PR #12973 applied 
(without PR #13044) and tested two scenarios:
   
   | Scenario | Without this PR (baseline) | With this PR |
   |---|---|---|
   | 1. Default Domain Admin → User account with Root Admin role | Account is 
created with role_id=1 (escalation succeeds) | Blocked with HTTP 531 + "role 
null or unknown" - the error message could be clearer |
   | 2. Restricted Domain Admin -> default Domain Admin account (#5781) | 
Escalated APIs available to the created account | Blocked with HTTP 531 + clear 
"Escalated APIs: [createServiceOffering]" message |
   
   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:**
   
   1. **The error message in Scenario 1 could be clearer.** "The account has 
role null or unknown" doesn't tell the user that this was an escalation 
attempt. Consider catching the null-role case in `getApisAllowedToAccount` and 
returning a clearer message like "cannot assign this role from your Domain 
Admin account context" - and if possible a 4xx HTTP code instead of 531.
   
   2. **Scenario 1's block currently depends on `findRole` hiding Admin roles 
from non-Admins.** If that visibility ever changes (for example, to support a 
UI feature listing roles), the new size comparison would be the only thing left 
protecting against this case - and with the default Domain Admin role's broad 
ALLOW rules, it might not catch it. Worth considering whether 
`validateRoleChange` (which already exists and runs in `updateAccount`) should 
also run in the `createAccount` path so there's a clear role-type check before 
the permission scan.
   
   ---
   
   ### Detailed test cases
   
   Build 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
   
   - Test domain: `test-pr12973` (id: `915effa9-4149-4afc-8987-dd37a858cf78`)
   - Default Domain Admin user: `tpr12973-domadmin` (account id: 
`7141f4b5-2781-4985-9207-01caef71b95c`, user id: 
`3e4f5ef9-944b-46a9-aa5c-f3497c0d153b`)
   - Restricted custom Domain Admin role: `tpr12973-da-restricted` (id: 
`572a677a-64a3-492d-9ed9-263c8ead139a`)
     - Permission 1 (sortorder 1): `createServiceOffering` DENY
     - Permission 2 (sortorder 2): `*` ALLOW
   - Restricted user with the role above: `tpr12973-da-restricted-user` 
(account id: `59fb08dd-e09c-4603-bdb5-e2d034419b71`, user id: 
`4c0585c9-3732-4c2b-8ec0-9e2ee6a11899`)
   - Default Root Admin role id: `f9de0c41-4270-11f1-9812-1e001100045b`
   - Default Domain Admin role id: `f9de56a1-4270-11f1-9812-1e001100045b`
   
   #### Scenario 1 - Default Domain Admin tries to create a User account with 
the Root Admin role
   
   Run as `tpr12973-domadmin` (default Domain Admin role).
   
   ```
   (tpr12973-domadmin) 🐱 > sync
   Discovered 420 APIs
   
   (tpr12973-domadmin) 🐱 > create account accounttype=0 
username=tc12973-escalation password=Password123 firstname=Test 
lastname=Escalation [email protected] 
domainid=915effa9-4149-4afc-8987-dd37a858cf78 
roleid=f9de0c41-4270-11f1-9812-1e001100045b
   🙈 Error: (HTTP 531, error code 4365) The account [Account 
[{"accountName":"tc12973-escalation","id":0,"uuid":"98338144-9c43-4940-bd99-dc64610955c6"}]]
 has role null or unknown.
   
   mysql> SELECT id, uuid, account_name FROM account WHERE 
account_name='tc12973-escalation';
   Empty set (0.00 sec)
   
   mysql> SELECT id, uuid, username FROM user WHERE 
username='tc12973-escalation';
   Empty set (0.00 sec)
   
   **Management server log:**
   
   2026-04-27 20:04:19,950 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-18:[ctx-8d78c1a7, ctx-a39d7d92, ctx-dc8c5b13]) (logid:9a87f5b8) 
Verifying whether the caller has the correct privileges based on the user's 
role type and API permissions: Account 
[{"accountName":"tc12973-escalation","id":0,"uuid":"98338144-9c43-4940-bd99-dc64610955c6"}]
   2026-04-27 20:04:19,951 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-18:[ctx-8d78c1a7, ctx-a39d7d92, ctx-dc8c5b13]) (logid:9a87f5b8) 
Checking calling account [tpr12973-domadmin, 
7141f4b5-2781-4985-9207-01caef71b95c] permission to perform this operation for 
user account [tc12973-escalation, 98338144-9c43-4940-bd99-dc64610955c6]
   2026-04-27 20:04:19,951 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-18:[ctx-8d78c1a7, ctx-a39d7d92, ctx-dc8c5b13]) (logid:9a87f5b8) 
Checking if user of account tpr12973-domadmin 
[7141f4b5-2781-4985-9207-01caef71b95c] with role-id [3] can create an account 
of type tc12973-escalation [98338144-9c43-4940-bd99-dc64610955c6] with role-id 
[1]
   2026-04-27 20:04:19,969 INFO  [c.c.a.ApiServer] 
(qtp2038105753-18:[ctx-8d78c1a7, ctx-a39d7d92, ctx-dc8c5b13]) (logid:9a87f5b8) 
PermissionDenied: The account [Account 
[{"accountName":"tc12973-escalation","id":0,"uuid":"98338144-9c43-4940-bd99-dc64610955c6"}]]
 has role null or unknown. on objs: []
   ```
   
   **Result:** The escalation is blocked. No rows in `account` or `user` tables.
   
   **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, 
`findRole` returns null because Admin roles are hidden from non-Admin callers. 
The new code throws on that null and stops the operation. So the escalation IS 
blocked, but it's blocked because the Domain Admin can't even see the Root 
Admin role - not because the new permission check decided it was an escalation. 
The actual size comparison (the new logic this PR adds) never runs in this 
scenario.
   
   On the baseline build, the same null-role exception happens, but the old 
code's `try/catch... continue` swallowed it for every API and let the loop 
finish, so the escalation went through. This PR removes that 
catch-and-continue, which is what closes the gap here - but as a side effect, 
not by design.
   
   #### Scenario 2 - Restricted custom Domain Admin tries to create a default 
Domain Admin account (issue #5781)
   
   Run as `tpr12973-da-restricted-user`. The role allows `*` and denies only 
`createServiceOffering`.
   
   ```
   (tpr12973-da-restricted-user) 🐱 > sync
   Discovered 878 APIs
   
   (tpr12973-da-restricted-user) 🐱 > list serviceofferings
   {
     "count": 2,
     "serviceoffering": [
       {
         "cpunumber": 1,
         "cpuspeed": 500,
         "created": "2026-04-27T19:41:43+0000",
         "defaultuse": false,
         "diskofferingdisplaytext": "Small Instance",
         "diskofferingid": "91c1d067-bbe1-457f-81dd-3fb8d66fb612",
         "diskofferingname": "Small Instance",
         "diskofferingstrictness": false,
         "displaytext": "Small Instance",
         "dynamicscalingenabled": true,
         "encryptroot": false,
         "hasannotations": false,
         "id": "81a91cf3-aa46-4612-9fbf-f8f2437e1d32",
         "iscustomized": false,
         "issystem": false,
         "isvolatile": false,
         "limitcpuuse": false,
         "memory": 512,
         "name": "Small Instance",
         "offerha": false,
         "provisioningtype": "thin",
         "rootdisksize": 0,
         "state": "Active",
         "storagetype": "shared"
       },
       {
         "cpunumber": 1,
         "cpuspeed": 1000,
         "created": "2026-04-27T19:41:43+0000",
         "defaultuse": false,
         "diskofferingdisplaytext": "Medium Instance",
         "diskofferingid": "d1557163-589a-47f5-981c-648861ded62c",
         "diskofferingname": "Medium Instance",
         "diskofferingstrictness": false,
         "displaytext": "Medium Instance",
         "dynamicscalingenabled": true,
         "encryptroot": false,
         "hasannotations": false,
         "id": "816dcd5c-f9a2-4657-a1e5-713a99750a09",
         "iscustomized": false,
         "issystem": false,
         "isvolatile": false,
         "limitcpuuse": false,
         "memory": 1024,
         "name": "Medium Instance",
         "offerha": false,
         "provisioningtype": "thin",
         "rootdisksize": 0,
         "state": "Active",
         "storagetype": "shared"
       }
     ]
   }
   
   (tpr12973-da-restricted-user) 🐱 > create serviceoffering 
name=tpr12973-test-offering displaytext=Test cpunumber=1 cpuspeed=1000 
memory=512
   🙈 Error: unknown command or API requested
   
   (tpr12973-da-restricted-user) 🐱 > create account accounttype=2 
username=tc12973-da-escalation password=Password123 firstname=Escaped 
lastname=DA [email protected] 
domainid=915effa9-4149-4afc-8987-dd37a858cf78 
roleid=f9de56a1-4270-11f1-9812-1e001100045b
   🙈 Error: (HTTP 531, error code 4365) User of Account Account 
[{"accountName":"tpr12973-da-restricted-user","id":6,"uuid":"59fb08dd-e09c-4603-bdb5-e2d034419b71"}]
 and domain Domain 
{"id":2,"name":"test-pr12973","path":"\/test-pr12973\/","uuid":"915effa9-4149-4afc-8987-dd37a858cf78"}
 cannot create an account with access to more privileges than they have. 
Escalated APIs: [createServiceOffering]
   
   mysql> SELECT id, uuid, account_name FROM account WHERE 
account_name='tc12973-da-escalation';
   Empty set (0.00 sec)
   
   mysql> SELECT id, uuid, username FROM user WHERE 
username='tc12973-da-escalation';
   Empty set (0.01 sec)
   
   **Management server log:**
   
   2026-04-27 20:12:09,227 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-19:[ctx-b111f5f0, ctx-992dd474, ctx-ee71b5ca]) (logid:3ffca8c0) 
Verifying whether the caller has the correct privileges based on the user's 
role type and API permissions: Account 
[{"accountName":"tc12973-da-escalation","id":0,"uuid":"3721d72d-0906-490d-9424-c1b4232a32a3"}]
   2026-04-27 20:12:09,230 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-19:[ctx-b111f5f0, ctx-992dd474, ctx-ee71b5ca]) (logid:3ffca8c0) 
Checking calling account [tpr12973-da-restricted-user, 
59fb08dd-e09c-4603-bdb5-e2d034419b71] permission to perform this operation for 
user account [tc12973-da-escalation, 3721d72d-0906-490d-9424-c1b4232a32a3]
   2026-04-27 20:12:09,230 DEBUG [c.c.u.AccountManagerImpl] 
(qtp2038105753-19:[ctx-b111f5f0, ctx-992dd474, ctx-ee71b5ca]) (logid:3ffca8c0) 
Checking if user of account tpr12973-da-restricted-user 
[59fb08dd-e09c-4603-bdb5-e2d034419b71] with role-id [10] can create an account 
of type tc12973-da-escalation [3721d72d-0906-490d-9424-c1b4232a32a3] with 
role-id [3]
   2026-04-27 20:12:09,605 WARN  [c.c.u.AccountManagerImpl] 
(qtp2038105753-19:[ctx-b111f5f0, ctx-992dd474, ctx-ee71b5ca]) (logid:3ffca8c0) 
User of Account Account 
[{"accountName":"tpr12973-da-restricted-user","id":6,"uuid":"59fb08dd-e09c-4603-bdb5-e2d034419b71"}]
 and domain Domain 
{"id":2,"name":"test-pr12973","path":"\/test-pr12973\/","uuid":"915effa9-4149-4afc-8987-dd37a858cf78"}
 cannot create an account with access to more privileges than they have. 
Escalated APIs: [createServiceOffering]
   2026-04-27 20:12:09,622 INFO  [c.c.a.ApiServer] 
(qtp2038105753-19:[ctx-b111f5f0, ctx-992dd474, ctx-ee71b5ca]) (logid:3ffca8c0) 
PermissionDenied: User of Account Account 
[{"accountName":"tpr12973-da-restricted-user","id":6,"uuid":"59fb08dd-e09c-4603-bdb5-e2d034419b71"}]
 and domain Domain 
{"id":2,"name":"test-pr12973","path":"\/test-pr12973\/","uuid":"915effa9-4149-4afc-8987-dd37a858cf78"}
 cannot create an account with access to more privileges than they have. 
Escalated APIs: [createServiceOffering] on objs: []
   ```
   
   **Result:** Blocked, with a clear and helpful error message that names 
exactly which API was the problem (`createServiceOffering`).
   
   The user sees `878` of `879` APIs (the one excluded by the deny rule), 
confirming the deny rule is enforced. DB confirms no rows persisted. 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to