Move checkRoleEscalation outside DB transaction in createAccount#13044
Move checkRoleEscalation outside DB transaction in createAccount#13044nvazquez wants to merge 1 commit intoapache:4.22from
Conversation
The read-only role escalation check iterates all API commands and does not need a write transaction open. Using a transient AccountVO for the check avoids holding the DB connection during the permission scan, reducing connection pool pressure and API latency.
|
@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. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 4.22 #13044 +/- ##
============================================
- Coverage 17.68% 17.68% -0.01%
+ Complexity 15793 15789 -4
============================================
Files 5922 5922
Lines 533096 533094 -2
Branches 65209 65209
============================================
- Hits 94275 94263 -12
- Misses 428181 428190 +9
- Partials 10640 10641 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17540 |
|
@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-15915)
|
|
@blueorangutan test |
|
@RosiKyu a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
@blueorangutan test |
|
@RosiKyu a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
There was a problem hiding this comment.
LGTM
Summary Test Execution Report
Test Results
| TC | Description | Result |
|---|---|---|
| TC01 | Create User account, default User role, no UUID | PASS |
| TC02 | Create User account with explicit UUID | PASS |
| TC03 | Create Domain Admin account, default Domain Admin role | PASS |
| TC04 | Create Root Admin account in ROOT domain | PASS |
| TC05 | Create Resource Domain Admin (accounttype=3) | PASS (with pre-existing accounttype 3 to 2 normalization noted) |
| TC06 | Create account with custom (restricted) role | PASS |
| TC07 | Create account with networkdomain set | PASS |
| TC08 | Create account with accountdetails map | PASS (CMK 6.5.0 client-side serialization quirk noted, out of scope) |
| TC09 | Domain admin creates account in own domain | PASS |
| TC10 | Domain Admin attempts to create account assigned Root Admin role (orphan-row check + cross-env behavior parity) | PASS (with pre-existing finding noted, out of scope) |
| TC11 | User with restricted role attempts to create account, denied at API access layer | PASS |
| TC12 | Exception type and message unchanged from baseline | PASS |
| TC13 | Concurrent valid + invalid creates, no deadlocks, no orphan rows | PASS |
| TC14 | Two rapid sequential creates without UUID, no collision | PASS |
| TC15 | Full account lifecycle (create, update, disable, delete) | PASS |
| TC16 | Create account with invalid roleId | PASS |
| TC17 | Create account with invalid domainId | PASS |
Notes and Observations
On TC08 (cmk behavior, out of scope)
When passing accountdetails[N].key=foo and accountdetails[N].value=bar syntax via cmk 6.5.0, the literal field names "key" and "value" are sent as the map keys instead of foo. This was reproduced on a baseline build without the current PR code and is therefore a CMK client-side issue, not a PR finding. The management server side of the contract is honored: whatever the details map contains, it is correctly propagated through createAccount after the PR's reorganization.
On TC10 (pre-existing role escalation finding)
During TC10 we found that a Domain Admin can successfully create a User-type account assigned the Root Admin role in their own domain. The escalation went through and the account was persisted with role_id=1. This was reproduced identically on a baseline build without this PR, confirming the issue is pre-existing and is not introduced by this PR. Community PR #12973 in apache/cloudstack (currently in milestone 4.22.2) addresses this in the access-check logic. PR #13044 only changes when checkRoleEscalation runs (before vs inside the DB transaction); PR #12973 changes how the check works internally. The two PRs change different things and don't conflict, both are useful, neither makes the other redundant.
On orphan rows
Across all negative-path tests (TC10, TC11, TC13, TC16, TC17), no orphan rows were ever observed in the account or user tables when a createAccount call was rejected. PR #13044's pre-transaction check ensures rejected creates do not produce partial DB writes. This was verified with explicit DB queries in every relevant test case.
On concurrency (TC13)
20 concurrent createAccount calls (10 valid + 10 invalid) completed without deadlocks, without orphan rows, and without any exception entries in the management-server log. The mgmt server remained responsive throughout. Total wall time ~32 seconds. The valid creates landed correctly with the requested role; the invalid creates were uniformly rejected with no DB side effects.
The pre-existing finding identified during TC10 is independent of this PR and is being addressed separately by community PR #12973.
Detailed Test Report
TC01: Create User account, default User role, no UUID
Description: Verify that a User-type account can be created without an explicit accountid, and that a valid UUID is auto-generated and persisted.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
Steps:
- From cmk as root admin, run createAccount without an
accountidparameter:
create account accounttype=0 username=tc01-user password=Password123 firstname=TC01 lastname=User email=tc01@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 - Verify the response contains a valid v4 UUID for
account.id. - Verify the first user is created and linked to the account.
Expected result:
- Account is created successfully (no exception).
account.idis a valid v4 UUID, auto-generated outside the transactionaccounttype=0,roletype=User,state=enabled.- First user
tc01-useris created anduser.accountidequalsaccount.id.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc01-user password=Password123 firstname=TC01 lastname=User email=tc01@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:00+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "fca3421a-d8e7-471a-be89-2e36d603f714",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc01-user",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc01-user",
"accountid": "fca3421a-d8e7-471a-be89-2e36d603f714",
"accounttype": 0,
"created": "2026-04-27T11:02:00+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc01@test.local",
"firstname": "TC01",
"id": "2ba2a4c8-2d9a-47fd-85c0-677f467ef926",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "User",
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"state": "enabled",
"username": "tc01-user",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Account
tc01-usercreated successfully. account.id=fca3421a-d8e7-471a-be89-2e36d603f714- valid v4 UUID, auto-generated by the PR's pre-transactionUUID.randomUUID().toString()call inresolvedAccountUUID.accounttype=0,roletype=User,state=enabled.- First user
tc01-user(id2ba2a4c8-2d9a-47fd-85c0-677f467ef926) created with matchingaccountid. - PASS
TC02: Create User account with explicit UUID
Description: Verify that when an explicit accountid is supplied, it is preserved and used for the account row instead of being replaced by an auto-generated UUID.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
- Pre-chosen test UUID: 11111111-2222-3333-4444-555555555555
Steps:
- From cmk as root admin, run createAccount with
accountid=11111111-2222-3333-4444-555555555555:
create account accounttype=0 username=tc02-user password=Password123 firstname=TC02 lastname=User email=tc02@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 accountid=11111111-2222-3333-4444-555555555555 - Verify the response contains the exact supplied UUID as
account.id.
Expected result:
- Account is created successfully.
account.idequals exactly11111111-2222-3333-4444-555555555555(the supplied UUID is preserved byresolvedAccountUUID).- The first user's
accountidmatches the supplied UUID.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc02-user password=Password123 firstname=TC02 lastname=User email=tc02@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 accountid=11111111-2222-3333-4444-555555555555
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:11+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "11111111-2222-3333-4444-555555555555",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc02-user",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc02-user",
"accountid": "11111111-2222-3333-4444-555555555555",
"accounttype": 0,
"created": "2026-04-27T11:02:12+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc02@test.local",
"firstname": "TC02",
"id": "f2e48496-c07c-41d2-a4c6-2fc59f3f7cee",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "User",
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"state": "enabled",
"username": "tc02-user",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Account
tc02-usercreated successfully. account.id=11111111-2222-3333-4444-555555555555- the supplied UUID is preserved exactly.user.accountidmatches the supplied UUID, confirming the UUID is consistently propagated through both account and user creation under the new pre-transaction resolution.- PASS
TC03: Create Domain Admin account, default Domain Admin role
Description: Verify that a Domain Admin (accounttype=2) account can be created with the default Domain Admin role.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default Domain Admin role (id: d6e03570-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
Steps:
- From cmk as root admin, run createAccount with
accounttype=2and the Domain Admin role:
create account accounttype=2 username=tc03-domadmin password=Password123 firstname=TC03 lastname=DomAdmin email=tc03@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e03570-4224-11f1-a47c-1e00450001b6 - Verify
accounttype=2androletype=DomainAdminin the response.
Expected result:
- Account is created successfully.
accounttype=2,rolename=Domain Admin,roletype=DomainAdmin.- First user is created and linked.
Test Evidence:
(localcloud) 🐱 > create account accounttype=2 username=tc03-domadmin password=Password123 firstname=TC03 lastname=DomAdmin email=tc03@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e03570-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 2,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:20+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "a8bc08ee-3ea1-42b2-b9cb-9ea1735a292e",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc03-domadmin",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e03570-4224-11f1-a47c-1e00450001b6",
"rolename": "Domain Admin",
"roletype": "DomainAdmin",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc03-domadmin",
"accountid": "a8bc08ee-3ea1-42b2-b9cb-9ea1735a292e",
"accounttype": 2,
"created": "2026-04-27T11:02:20+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc03@test.local",
"firstname": "TC03",
"id": "ea0ea3d5-97e6-4370-bfc4-cb495f89712c",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "DomAdmin",
"roleid": "d6e03570-4224-11f1-a47c-1e00450001b6",
"rolename": "Domain Admin",
"roletype": "DomainAdmin",
"state": "enabled",
"username": "tc03-domadmin",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Account
tc03-domadmincreated successfully with ida8bc08ee-3ea1-42b2-b9cb-9ea1735a292e. accounttype=2,rolename=Domain Admin,roletype=DomainAdmin,state=enabled.- First user
tc03-domadmin(idea0ea3d5-97e6-4370-bfc4-cb495f89712c) created and linked. - PASS
TC04: Create Root Admin account in ROOT domain
Description: Verify that a Root Admin (accounttype=1) account can be created in the ROOT domain with the default Root Admin role. As caller is already root admin, the role escalation check (now performed before the transaction) must allow this creation.
Prerequisites:
- ROOT domain (id: afa4aae4-4224-11f1-a47c-1e00450001b6)
- Default Root Admin role (id: d6dfd5cf-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
Steps:
- From cmk as root admin, run createAccount with
accounttype=1and the Root Admin role in the ROOT domain:
create account accounttype=1 username=tc04-rootadmin password=Password123 firstname=TC04 lastname=RootAdmin email=tc04@test.local domainid=afa4aae4-4224-11f1-a47c-1e00450001b6 roleid=d6dfd5cf-4224-11f1-a47c-1e00450001b6 - Verify
accounttype=1androletype=Adminin the response.
Expected result:
- Account is created successfully (root admin can create root admin).
accounttype=1,rolename=Root Admin,roletype=Admin.- Limits show as
Unlimited(root admin is unrestricted).
Test Evidence:
(localcloud) 🐱 > create account accounttype=1 username=tc04-rootadmin password=Password123 firstname=TC04 lastname=RootAdmin email=tc04@test.local domainid=afa4aae4-4224-11f1-a47c-1e00450001b6 roleid=d6dfd5cf-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 1,
"apikeyaccess": "INHERIT",
"backupavailable": "Unlimited",
"backuplimit": "Unlimited",
"backupstorageavailable": "Unlimited",
"backupstoragelimit": "Unlimited",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "Unlimited",
"bucketlimit": "Unlimited",
"buckettotal": 0,
"cpuavailable": "Unlimited",
"cpulimit": "Unlimited",
"cputotal": 0,
"created": "2026-04-27T11:02:23+0000",
"domain": "ROOT",
"domainid": "afa4aae4-4224-11f1-a47c-1e00450001b6",
"domainpath": "ROOT",
"gpuavailable": "Unlimited",
"gpulimit": "Unlimited",
"gputotal": 0,
"groups": [],
"id": "be37f999-793d-4d56-8997-44547d6fe0d8",
"ipavailable": "Unlimited",
"iplimit": "Unlimited",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "Unlimited",
"memorylimit": "Unlimited",
"memorytotal": 0,
"name": "tc04-rootadmin",
"networkavailable": "Unlimited",
"networklimit": "Unlimited",
"networktotal": 0,
"objectstorageavailable": "Unlimited",
"objectstoragelimit": "Unlimited",
"objectstoragetotal": 0,
"primarystorageavailable": "Unlimited",
"primarystoragelimit": "Unlimited",
"primarystoragetotal": 0,
"projectavailable": "Unlimited",
"projectlimit": "Unlimited",
"projecttotal": 0,
"roleid": "d6dfd5cf-4224-11f1-a47c-1e00450001b6",
"rolename": "Root Admin",
"roletype": "Admin",
"secondarystorageavailable": "Unlimited",
"secondarystoragelimit": "Unlimited",
"secondarystoragetotal": 0,
"snapshotavailable": "Unlimited",
"snapshotlimit": "Unlimited",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "Unlimited",
"templatelimit": "Unlimited",
"templatetotal": 0,
"user": [
{
"account": "tc04-rootadmin",
"accountid": "be37f999-793d-4d56-8997-44547d6fe0d8",
"accounttype": 1,
"created": "2026-04-27T11:02:23+0000",
"domain": "ROOT",
"domainid": "afa4aae4-4224-11f1-a47c-1e00450001b6",
"email": "tc04@test.local",
"firstname": "TC04",
"id": "eef1411e-504f-44ff-925d-0bcabe76d184",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "RootAdmin",
"roleid": "d6dfd5cf-4224-11f1-a47c-1e00450001b6",
"rolename": "Root Admin",
"roletype": "Admin",
"state": "enabled",
"username": "tc04-rootadmin",
"usersource": "native"
}
],
"vmavailable": "Unlimited",
"vmlimit": "Unlimited",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "Unlimited",
"volumelimit": "Unlimited",
"volumetotal": 0,
"vpcavailable": "Unlimited",
"vpclimit": "Unlimited",
"vpctotal": 0
}
}
Actual result:
- Account
tc04-rootadmincreated successfully with idbe37f999-793d-4d56-8997-44547d6fe0d8in the ROOT domain. accounttype=1,rolename=Root Admin,roletype=Admin,state=enabled.- All resource limits show
Unlimited(consistent with root admin). - First user
tc04-rootadmincreated and linked. - The pre-transaction
checkRoleEscalationcorrectly allowed root admin → root admin (no escalation). - PASS
TC05: Create Resource Domain Admin (accounttype=3)
Description: Verify account creation with accounttype=3 (Resource Domain Admin) succeeds.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default Domain Admin role (id: d6e03570-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
Steps:
- From cmk as root admin, run createAccount with
accounttype=3:
create account accounttype=3 username=tc05-resdomadmin password=Password123 firstname=TC05 lastname=ResDomAdmin email=tc05@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e03570-4224-11f1-a47c-1e00450001b6 - Verify the account is created without exception.
Expected result:
- Account is created successfully.
- The account is linked to the Domain Admin role. (The reported
accounttypemay be normalized to 2 because the assigned role isroletype=DomainAdmin- this normalization predates the PR and is unrelated to the change being tested.)
Test Evidence:
(localcloud) 🐱 > create account accounttype=3 username=tc05-resdomadmin password=Password123 firstname=TC05 lastname=ResDomAdmin email=tc05@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e03570-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 2,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:38+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "670a3e68-1daf-4e3c-b1ac-0ca868e01901",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc05-resdomadmin",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e03570-4224-11f1-a47c-1e00450001b6",
"rolename": "Domain Admin",
"roletype": "DomainAdmin",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc05-resdomadmin",
"accountid": "670a3e68-1daf-4e3c-b1ac-0ca868e01901",
"accounttype": 2,
"created": "2026-04-27T11:02:38+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc05@test.local",
"firstname": "TC05",
"id": "39ea1fc8-71ed-458c-a3b8-b0b40991d0b9",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "ResDomAdmin",
"roleid": "d6e03570-4224-11f1-a47c-1e00450001b6",
"rolename": "Domain Admin",
"roletype": "DomainAdmin",
"state": "enabled",
"username": "tc05-resdomadmin",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Account
tc05-resdomadmincreated successfully with id670a3e68-1daf-4e3c-b1ac-0ca868e01901. - Reported
accounttype=2(normalized from the requestedaccounttype=3because the assigned role isDomainAdmin). This normalization is pre-existing CloudStack behavior unrelated to this PR - First user created and linked.
- PASS (with the noted pre-existing normalization)
TC06: Create account with custom (restricted) role
Description: Verify that account creation with a custom (non-default) role succeeds and the role is correctly persisted.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Custom role
tpr13044-restricted(id: 02401b36-e85d-4981-a433-3a49845df70c, type=User, onlylistVirtualMachinesallowed) - Caller: root admin
Steps:
- From cmk as root admin, run createAccount assigning the custom role:
create account accounttype=0 username=tc06-customrole password=Password123 firstname=TC06 lastname=Custom email=tc06@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=02401b36-e85d-4981-a433-3a49845df70c - Verify
roleidandrolenamein the response match the custom role.
Expected result:
- Account is created successfully.
roleid=02401b36-e85d-4981-a433-3a49845df70c,rolename=tpr13044-restricted,roletype=User.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc06-customrole password=Password123 firstname=TC06 lastname=Custom email=tc06@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=02401b36-e85d-4981-a433-3a49845df70c
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:41+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "346785d6-a506-412b-b8ea-b5fd9dfad349",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc06-customrole",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "02401b36-e85d-4981-a433-3a49845df70c",
"rolename": "tpr13044-restricted",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc06-customrole",
"accountid": "346785d6-a506-412b-b8ea-b5fd9dfad349",
"accounttype": 0,
"created": "2026-04-27T11:02:42+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc06@test.local",
"firstname": "TC06",
"id": "1d58fcb5-5450-464d-9391-12c22cb65934",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "Custom",
"roleid": "02401b36-e85d-4981-a433-3a49845df70c",
"rolename": "tpr13044-restricted",
"roletype": "User",
"state": "enabled",
"username": "tc06-customrole",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Account
tc06-customrolecreated successfully with id346785d6-a506-412b-b8ea-b5fd9dfad349. roleid=02401b36-e85d-4981-a433-3a49845df70c,rolename=tpr13044-restricted,roletype=User- custom role correctly persisted.- First user created and linked.
- PASS
TC07: Create account with networkdomain set
Description: Verify that networkdomain is correctly persisted to the account.network_domain column.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
- DB access on management server
Steps:
- From cmk as root admin, run createAccount with
networkdomain=tc07.test.local:
create account accounttype=0 username=tc07-netdom password=Password123 firstname=TC07 lastname=NetDom email=tc07@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 networkdomain=tc07.test.local - Verify
networkdomainis reflected in the API response. - Verify the
network_domaincolumn in the DB matches.
Expected result:
- Account is created successfully.
- API response contains
networkdomain=tc07.test.local. - DB
account.network_domaincolumn equalstc07.test.local. account.uuidmatchesaccount.idin the API response.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc07-netdom password=Password123 firstname=TC07 lastname=NetDom email=tc07@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 networkdomain=tc07.test.local
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:49+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "c917c5be-5cbf-4992-8266-e79e7a1547c3",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc07-netdom",
"networkavailable": "20",
"networkdomain": "tc07.test.local",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc07-netdom",
"accountid": "c917c5be-5cbf-4992-8266-e79e7a1547c3",
"accounttype": 0,
"created": "2026-04-27T11:02:49+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc07@test.local",
"firstname": "TC07",
"id": "421739d1-4332-42ee-9d7a-9269b948f7aa",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "NetDom",
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"state": "enabled",
"username": "tc07-netdom",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
- DB results
mysql> SELECT id, uuid, account_name, network_domain FROM account WHERE account_name='tc07-netdom';
+----+--------------------------------------+--------------+-----------------+
| id | uuid | account_name | network_domain |
+----+--------------------------------------+--------------+-----------------+
| 13 | c917c5be-5cbf-4992-8266-e79e7a1547c3 | tc07-netdom | tc07.test.local |
+----+--------------------------------------+--------------+-----------------+
1 row in set (0.00 sec)
Actual result:
- Account
tc07-netdomcreated successfully with idc917c5be-5cbf-4992-8266-e79e7a1547c3. - API response includes
networkdomain=tc07.test.local. - DB confirms
account.network_domain='tc07.test.local'andaccount.uuid='c917c5be-5cbf-4992-8266-e79e7a1547c3'matching the API-reportedaccount.id. - The pre-transaction UUID resolution and the persisted
network_domainvalue are consistent. - PASS
TC08: Create account with accountdetails map
Description: Verify that account creation with an accountdetails map persists details rows in the account_details table.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Caller: root admin
- DB access on management server
- Reference un-patched env for cross-validation
Steps:
- From cmk as root admin on patched env, run createAccount with two
accountdetails[N].key/accountdetails[N].valuepairs:
create account accounttype=0 username=tc08-details password=Password123 firstname=TC08 lastname=Details email=tc08@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 accountdetails[0].key=foo accountdetails[0].value=bar accountdetails[1].key=env accountdetails[1].value=test - Verify the account is created and the response contains the
accountdetailsfield. - Query
account_detailsin the DB. - Cross-validate by running the same exact CMK command against the un-patched env
Expected result:
- Account is created successfully on patched env.
account_detailsrows are persisted for the new account.- UUID consistency:
account.uuidin DB matchesaccount.idin API response. - DB rows on patched env match DB rows on un-patched env
Test Evidence:
Patched env:
(localcloud) 🐱 > create account accounttype=0 username=tc08-details password=Password123 firstname=TC08 lastname=Details email=tc08@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 accountdetails[0].key=foo accountdetails[0].value=bar accountdetails[1].key=env accountdetails[1].value=test
{
"account": {
"accountdetails": {
"key": "foo",
"value": "bar"
},
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:02:55+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "118c2841-e9c9-4e46-affa-e2b3d040beab",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc08-details",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc08-details",
"accountid": "118c2841-e9c9-4e46-affa-e2b3d040beab",
"accounttype": 0,
"created": "2026-04-27T11:02:55+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc08@test.local",
"firstname": "TC08",
"id": "310c8704-7efc-4fc0-a715-d77b51ccbc44",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "Details",
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"state": "enabled",
"username": "tc08-details",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
- DB results
mysql> SELECT account_id, name, value FROM account_details WHERE account_id IN (SELECT id FROM account WHERE account_name='tc08-details');
+------------+-------+-------+
| account_id | name | value |
+------------+-------+-------+
| 14 | value | bar |
| 14 | key | foo |
+------------+-------+-------+
2 rows in set (0.00 sec)
- Un-patched env, same CMK command, same client version:
(localcloud) 🐱 > create account accounttype=0 username=cmk-test-detail password=Password123 firstname=Test lastname=Detail email=test@test.local domainid=41e1cd1d-420a-11f1-ad94-1e001e000468 roleid=63378b83-420a-11f1-ad94-1e001e000468 accountdetails[0].key=foo accountdetails[0].value=bar accountdetails[1].key=env accountdetails[1].value=test
{
"account": {
"accountdetails": {
"key": "foo",
"value": "bar"
},
"accounttype": 0,
"id": "144b0c3b-a336-47fb-b284-a16a560e60d4",
"name": "cmk-test-detail",
...
}
}
mysql> SELECT account_id, name, value FROM account_details WHERE account_id IN (SELECT id FROM account WHERE account_name='cmk-test-detail');
+------------+-------+-------+
| account_id | name | value |
+------------+-------+-------+
| 5 | value | bar |
| 5 | key | foo |
+------------+-------+-------+
2 rows in set (0.00 sec)
Actual result:
- Account
tc08-detailscreated successfully with id118c2841-e9c9-4e46-affa-e2b3d040beab(DB id 14). account_detailsrows persisted (2 rows).- The literal
namevalues stored arekeyandvaluerather thanfooandenv. Reproducible on a baseline build without the change, so this is a CMK input handling detail and out of scope. - The
detailsmap is correctly propagated throughcreateAccount, persisted inaccount_details, and round-tripped in the API response. - UUID consistency confirmed: API
account.id= DBaccount.uuid. - PASS
TC09: Domain admin creates account in own domain
Description: Verify that a Domain Admin can create a User-type account in their own domain (no escalation, check must pass).
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Domain admin
tpr13044-domadmin(registered API keys, profile loaded into cmk) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Caller: tpr13044-domadmin (Domain Admin)
Steps:
- Switch cmk to the
tpr13044-domadminprofile - Verify authentication is correct via
list users listall=true- should return only domain-scoped users. - Run createAccount as the domain admin:
create account accounttype=0 username=tc09-user password=Password123 firstname=TC09 lastname=User email=tc09@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
Expected result:
- Authentication scope is correct: Domain Admin sees ~420 APIs (vs ~880 for root admin) and only test-pr13044 users.
- Account is created successfully - pre-transaction
checkRoleEscalationallows Domain Admin to assign the User role (no escalation). accounttype=0,rolename=User,state=enabled.
Test Evidence:
(tpr13044-domadmin) 🐱 > sync
Discovered 420 APIs
(tpr13044-domadmin) 🐱 > list users listall=true
{
"count": 9,
... (9 users, all in test-pr13044 domain - admin/ROOT not visible) ...
}
(tpr13044-domadmin) 🐱 > create account accounttype=0 username=tc09-user password=Password123 firstname=TC09 lastname=User email=tc09@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T11:05:43+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "beb17dda-1e38-4617-aa2a-fcae29721f9a",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc09-user",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc09-user",
"accountid": "beb17dda-1e38-4617-aa2a-fcae29721f9a",
"accounttype": 0,
"created": "2026-04-27T11:05:44+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc09@test.local",
"firstname": "TC09",
"id": "e5723783-7641-4adb-91ea-8ebd36330c3d",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "User",
"roleid": "d6e04f12-4224-11f1-a47c-1e00450001b6",
"rolename": "User",
"roletype": "User",
"state": "enabled",
"username": "tc09-user",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
Actual result:
- Domain admin profile authenticates with correctly reduced scope (420 APIs visible).
- Account
tc09-usercreated successfully (idbeb17dda-1e38-4617-aa2a-fcae29721f9a). accounttype=0,rolename=User,roletype=User,state=enabled.- First user created and linked.
- Pre-transaction
checkRoleEscalationcorrectly allowed the Domain Admin to assign the User role. - PASS
TC10: Domain Admin attempts to create account assigned Root Admin role - orphan-row + cross-env behavior parity
Description: Verify observable behavior is unchanged when a Domain Admin attempts to assign the Root Admin role to a new account.
Prerequisites:
- Patched env
- Un-patched reference env
- Patched env: domain admin
tpr13044-domadmin(account id: c2a8578c-e845-44c8-b21c-dc8625ff8f9f) intest-pr13044domain (id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Un-patched env: domain admin
ref-domadmin(account id: 636c59a2-379d-4c26-a73c-10d2bf6f1979) intest-pr13044-unpatcheddomain (id: 013d643f-cc9e-49c4-8967-44df98363afc) - DB access on both mgmt servers
Steps:
- On the patched env, as
tpr13044-domadminDomain Admin, attempt to create a User-type account assigned the Root Admin role:
create account accounttype=0 username=tc13044-escalation-v2 password=Password123 firstname=TC13044 lastname=Escalation email=tc13044-escalation-v2@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6dfd5cf-4224-11f1-a47c-1e00450001b6 - Capture API response. Query DB to confirm the account/user state and the persisted role.
- Grep management-server log for
checkRoleEscalationinvocation. - Reproduce identically on un-patched env: as
ref-domadminDomain Admin, attempt to create a User-type account assigned the Root Admin role with the un-patched env's IDs. - Capture and compare DB state and log output.
Expected result:
- The same observable behavior on both envs (PR #13044 only moves the call site, does not change check semantics).
- No orphan rows on either env (transaction integrity preserved).
checkRoleEscalationis invoked on both envs and arrives at the same verdict.
Test Evidence:
=== Patched env ===
(tpr13044-domadmin) 🐱 > create account accounttype=0 username=tc13044-escalation-v2 password=Password123 firstname=TC13044 lastname=Escalation email=tc13044-escalation-v2@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6dfd5cf-4224-11f1-a47c-1e00450001b6
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T12:08:28+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"domainpath": "ROOT/test-pr13044",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "c78a5131-5c4b-4606-a61b-fa5acf2fe963",
"ipavailable": "18",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "tc13044-escalation-v2",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "tc13044-escalation-v2",
"accountid": "c78a5131-5c4b-4606-a61b-fa5acf2fe963",
"accounttype": 0,
"created": "2026-04-27T12:08:29+0000",
"domain": "test-pr13044",
"domainid": "415b04d7-8251-4ee1-ac43-162d4668be52",
"email": "tc13044-escalation-v2@test.local",
"firstname": "TC13044",
"id": "bfb9b0e8-ba19-47ec-aaf2-71599348d6c0",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "Escalation",
"state": "enabled",
"username": "tc13044-escalation-v2",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
mysql> SELECT a.id, a.account_name, a.role_id, r.uuid AS role_uuid, r.name AS role_name, r.role_type FROM account a LEFT JOIN roles r ON a.role_id = r.id WHERE a.account_name='tc13044-escalation-v2';
+----+-----------------------+---------+--------------------------------------+------------+-----------+
| id | account_name | role_id | role_uuid | role_name | role_type |
+----+-----------------------+---------+--------------------------------------+------------+-----------+
| 16 | tc13044-escalation-v2 | 1 | d6dfd5cf-4224-11f1-a47c-1e00450001b6 | Root Admin | Admin |
+----+-----------------------+---------+--------------------------------------+------------+-----------+
1 row in set (0.00 sec)
Patched env management-server log:
2026-04-27 12:08:27,333 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Checking if user of account tpr13044-domadmin [c2a8578c-e845-44c8-b21c-dc8625ff8f9f] with role-id [3] can create an account of type tc13044-escalation-v2 [c78a5131-5c4b-4606-a61b-fa5acf2fe963] with role-id [1]
2026-04-27 12:08:28,086 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Verifying whether the caller has the correct privileges based on the user's role type and API permissions: Account [{"accountName":"tc13044-escalation-v2","id":0,"uuid":"c78a5131-5c4b-4606-a61b-fa5acf2fe963"}]
2026-04-27 12:08:28,087 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Checking calling account [tpr13044-domadmin, c2a8578c-e845-44c8-b21c-dc8625ff8f9f] permission to perform this operation for user account [tc13044-escalation-v2, c78a5131-5c4b-4606-a61b-fa5acf2fe963]
2026-04-27 12:08:28,087 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Checking if user of account tpr13044-domadmin [c2a8578c-e845-44c8-b21c-dc8625ff8f9f] with role-id [3] can create an account of type tc13044-escalation-v2 [c78a5131-5c4b-4606-a61b-fa5acf2fe963] with role-id [1]
2026-04-27 12:08:28,812 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Calling account [tpr13044-domadmin, c2a8578c-e845-44c8-b21c-dc8625ff8f9f] is allowed to perform this operation for user account [tc13044-escalation-v2, c78a5131-5c4b-4606-a61b-fa5acf2fe963]
2026-04-27 12:08:28,828 DEBUG [c.c.n.s.SecurityGroupManagerImpl2] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Created security group SecurityGroup {"id":14,"name":"default","uuid":"2c7fc864-f208-4d38-90d0-99aab1b0c9c2"} for account [id: 16, name: tc13044-escalation-v2]
2026-04-27 12:08:28,828 DEBUG [c.c.u.AccountManagerImpl] (qtp1438988851-17:[ctx-54d9b52e, ctx-94b04ca5, ctx-90354f6f]) (logid:30831fbd) Creating user: tc13044-escalation-v2, accountId: 16 timezone:null
=== Un-patched reference env ===
Same exact escalation attempt with un-patched env's IDs:
(ref-domadmin) 🐱 > create account accounttype=0 username=ref-escalation-v2 password=Password123 firstname=Ref lastname=Escalation email=ref-esc-v2@test.local domainid=013d643f-cc9e-49c4-8967-44df98363afc roleid=63376c5f-420a-11f1-ad94-1e001e000468
{
"account": {
"accounttype": 0,
"apikeyaccess": "INHERIT",
"backupavailable": "20",
"backuplimit": "20",
"backupstorageavailable": "400",
"backupstoragelimit": "400",
"backupstoragetotal": 0,
"backuptotal": 0,
"bucketavailable": "20",
"bucketlimit": "20",
"buckettotal": 0,
"cpuavailable": "40",
"cpulimit": "40",
"cputotal": 0,
"created": "2026-04-27T12:13:54+0000",
"domain": "test-pr13044-unpatched",
"domainid": "013d643f-cc9e-49c4-8967-44df98363afc",
"domainpath": "ROOT/test-pr13044-unpatched",
"gpuavailable": "20",
"gpulimit": "20",
"gputotal": 0,
"groups": [],
"id": "445341bd-9b7a-4fc0-9d60-d8427f311ba1",
"ipavailable": "17",
"iplimit": "20",
"iptotal": 0,
"isdefault": false,
"memoryavailable": "40960",
"memorylimit": "40960",
"memorytotal": 0,
"name": "ref-escalation-v2",
"networkavailable": "20",
"networklimit": "20",
"networktotal": 0,
"objectstorageavailable": "400",
"objectstoragelimit": "400",
"objectstoragetotal": 0,
"primarystorageavailable": "200",
"primarystoragelimit": "200",
"primarystoragetotal": 0,
"projectavailable": "10",
"projectlimit": "10",
"projecttotal": 0,
"secondarystorageavailable": "400.0",
"secondarystoragelimit": "400",
"secondarystoragetotal": 0,
"snapshotavailable": "20",
"snapshotlimit": "20",
"snapshottotal": 0,
"state": "enabled",
"templateavailable": "20",
"templatelimit": "20",
"templatetotal": 0,
"user": [
{
"account": "ref-escalation-v2",
"accountid": "445341bd-9b7a-4fc0-9d60-d8427f311ba1",
"accounttype": 0,
"created": "2026-04-27T12:13:55+0000",
"domain": "test-pr13044-unpatched",
"domainid": "013d643f-cc9e-49c4-8967-44df98363afc",
"email": "ref-esc-v2@test.local",
"firstname": "Ref",
"id": "89cc6137-99c0-48fb-8d11-050003976733",
"is2faenabled": false,
"is2famandated": false,
"iscallerchilddomain": false,
"isdefault": false,
"lastname": "Escalation",
"state": "enabled",
"username": "ref-escalation-v2",
"usersource": "native"
}
],
"vmavailable": "20",
"vmlimit": "20",
"vmrunning": 0,
"vmstopped": 0,
"vmtotal": 0,
"volumeavailable": "20",
"volumelimit": "20",
"volumetotal": 0,
"vpcavailable": "20",
"vpclimit": "20",
"vpctotal": 0
}
}
mysql> SELECT a.id, a.account_name, a.role_id, r.uuid AS role_uuid, r.name AS role_name, r.role_type FROM account a LEFT JOIN roles r ON a.role_id = r.id WHERE a.account_name='ref-escalation-v2';
+----+-------------------+---------+--------------------------------------+------------+-----------+
| id | account_name | role_id | role_uuid | role_name | role_type |
+----+-------------------+---------+--------------------------------------+------------+-----------+
| 7 | ref-escalation-v2 | 1 | 63376c5f-420a-11f1-ad94-1e001e000468 | Root Admin | Admin |
+----+-------------------+---------+--------------------------------------+------------+-----------+
1 row in set (0.00 sec)
Un-patched env management-server log:
2026-04-27 12:13:54,054 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Verifying whether the caller has the correct privileges based on the user's role type and API permissions: Account [{"accountName":"ref-escalation-v2","id":0,"uuid":"445341bd-9b7a-4fc0-9d60-d8427f311ba1"}]
2026-04-27 12:13:54,055 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Checking calling account [ref-domadmin, 636c59a2-379d-4c26-a73c-10d2bf6f1979] permission to perform this operation for user account [ref-escalation-v2, 445341bd-9b7a-4fc0-9d60-d8427f311ba1]
2026-04-27 12:13:54,055 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Checking if user of account ref-domadmin [636c59a2-379d-4c26-a73c-10d2bf6f1979] with role-id [3] can create an account of type ref-escalation-v2 [445341bd-9b7a-4fc0-9d60-d8427f311ba1] with role-id [1]
2026-04-27 12:13:54,462 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Calling account [ref-domadmin, 636c59a2-379d-4c26-a73c-10d2bf6f1979] is allowed to perform this operation for user account [ref-escalation-v2, 445341bd-9b7a-4fc0-9d60-d8427f311ba1]
2026-04-27 12:13:54,471 DEBUG [c.c.n.s.SecurityGroupManagerImpl2] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Created security group SecurityGroup {"id":5,"name":"default","uuid":"0c51d91d-946a-4b53-ae66-663cafd7228e"} for account [id: 7, name: ref-escalation-v2]
2026-04-27 12:13:54,471 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Checking if user of account ref-domadmin [636c59a2-379d-4c26-a73c-10d2bf6f1979] with role-id [3] can create an account of type ref-escalation-v2 [445341bd-9b7a-4fc0-9d60-d8427f311ba1] with role-id [1]
2026-04-27 12:13:54,860 DEBUG [c.c.u.AccountManagerImpl] (qtp2038105753-18:[ctx-ee83a20e, ctx-b58e1f18, ctx-0e236b8c]) (logid:03c271cd) Creating user: ref-escalation-v2, accountId: 7 timezone:null
Actual result:
- Patched env: account
tc13044-escalation-v2created withrole_id=1(Root Admin) persisted in DB. - Un-patched env: account
ref-escalation-v2created withrole_id=1(Root Admin) persisted in DB. checkRoleEscalationis invoked on both envs and reaches the same verdict ("is allowed to perform this operation").- Behavior is byte-for-byte identical between patched and un-patched builds.
- PASS - observable behavior is preserved.
Note (out of scope): A pre-existing bug in checkRoleEscalation lets a Domain Admin assign the Root Admin role to a User-type account in their own domain. Reproducible on baseline builds, so not introduced here. Community PR #12973 addresses it.
TC11: User with restricted role attempts to create account - denied at API access layer
Description: Verify that a user with a restricted role (only listVirtualMachines allowed) is blocked from invoking createAccount at the API access layer.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6)
- Custom restricted role
tpr13044-restricted(id: 02401b36-e85d-4981-a433-3a49845df70c, type=User, onlylistVirtualMachinesallowed) - Restricted user account
tpr13044-restricteduser(account id: 8c0b4fd9-435d-4dd6-978a-71653c6aa625, user id: 76669853-043e-4d90-837a-aed2337a984d) with API keys - Caller: tpr13044-restricteduser (custom restricted role)
Steps:
- Switch cmk to the
tpr13044-restricteduserprofile - Verify scope: 127 APIs visible (custom-role permitted set + baseline auto-allowed APIs like login/logout/listApis).
- Sanity check:
list virtualmachines(the only explicitly allowed business API). - Attempt to create an account with the User role:
create account accounttype=0 username=tc13044-restricted-create password=Password123 firstname=TC13044 lastname=Restricted email=tc13044-r@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6 - Verify the call is rejected.
- Verify DB: zero rows in
accountandusertables for the attempted username.
Expected result:
- API count is reduced to the restricted role's permitted set + baseline (much smaller than full DA or admin scopes).
list virtualmachinessucceeds.create accountis rejected -createAccountis not in the role's allowed APIs.- No rows persisted in
accountorusertables.
Test Evidence:
(tpr13044-restricteduser) 🐱 > sync
Discovered 127 APIs
(tpr13044-restricteduser) 🐱 > list virtualmachines
(tpr13044-restricteduser) 🐱 >
(tpr13044-restricteduser) 🐱 > create account accounttype=0 username=tc13044-restricted-create password=Password123 firstname=TC13044 lastname=Restricted email=tc13044-r@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
🙈 Error: unknown command or API requested
mysql> SELECT id, uuid, account_name FROM account WHERE account_name='tc13044-restricted-create';
Empty set (0.00 sec)
mysql> SELECT id, uuid, username FROM user WHERE username='tc13044-restricted-create';
Empty set (0.00 sec)
Actual result:
- Restricted role's scope correctly enforced: 127 APIs visible (
listVirtualMachinesplus baseline APIs like login/logout/listApis). list virtualmachinessucceeded (empty result).create accountrejected with "unknown command or API requested" — denied at the API access layer before reaching account-creation logic.- DB confirms no rows persisted in
accountoruser. - PASS
TC12: Exception type and message unchanged from baseline
Description: Verify exception type, error text, and HTTP status returned by checkRoleEscalation are unchanged.
Prerequisites:
- Patched env and un-patched env, same KVM/4.22.1.0 base
- Same scenario as TC10 (Domain Admin assigning Root Admin role to a User account)
Steps:
- Compare the API response and log line from TC10 on both envs.
Expected result:
- Same HTTP status, same log message format, same fields.
Test Evidence:
Patched env (PR 13044):
2026-04-27 12:08:28,812 DEBUG [c.c.u.AccountManagerImpl] Calling account [tpr13044-domadmin, c2a8578c-e845-44c8-b21c-dc8625ff8f9f] is allowed to perform this operation for user account [tc13044-escalation-v2, c78a5131-5c4b-4606-a61b-fa5acf2fe963]
HTTP 200, account row created.
Un-patched env:
2026-04-27 12:13:54,462 DEBUG [c.c.u.AccountManagerImpl] Calling account [ref-domadmin, 636c59a2-379d-4c26-a73c-10d2bf6f1979] is allowed to perform this operation for user account [ref-escalation-v2, 445341bd-9b7a-4fc0-9d60-d8427f311ba1]
HTTP 200, account row created.
Actual result:
- Both envs return HTTP 200 with identical log message format. No regression in error reporting introduced by PR 13044.
- PASS
TC13: Concurrent valid + invalid creates - no deadlocks, no orphan rows
Description: Stress the createAccount path with 20 parallel API calls (10 valid, 10 escalation attempts) to confirm no deadlocks or orphan rows under concurrent load.
Prerequisites:
- Test domain
test-pr13044(id: 415b04d7-8251-4ee1-ac43-162d4668be52) - Default User role (id: d6e04f12-4224-11f1-a47c-1e00450001b6, internal id 4)
- Default Root Admin role (id: d6dfd5cf-4224-11f1-a47c-1e00450001b6, internal id 1)
- Admin API keys (re-registered fresh for this test)
- Python script using HMAC-signed direct API calls with ThreadPoolExecutor (max_workers=20) tc13_parallel.py
Steps:
- Fire 20 concurrent createAccount calls: 10 with username
tc13044-par-valid-NNand roleid=User; 10 with usernametc13044-par-escal-NNand roleid=Root Admin (in the non-ROOT test domain to trigger domain-validation rejection). - Verify status of each call (success/failure with timing).
- Query DB for created accounts and confirm role_id values.
- Grep management-server log for deadlock or exception entries related to the test usernames.
Expected result:
- All 10 valid creates succeed (HTTP 200) with User role assigned.
- All 10 escalation attempts are denied with no DB rows persisted.
- No deadlocks, no NPEs, no rolled-back partial writes in the log.
Test Evidence:
Firing 20 concurrent createAccount calls...
Total wall time: 32.30s
Username Kind Status Time
------------------------------------------------------------
tc13044-par-valid-00 valid OK 32.02s
tc13044-par-valid-01 valid OK 31.76s
tc13044-par-valid-02 valid OK 31.98s
tc13044-par-valid-03 valid OK 32.29s
tc13044-par-valid-04 valid OK 32.26s
tc13044-par-valid-05 valid OK 31.48s
tc13044-par-valid-06 valid OK 32.04s
tc13044-par-valid-07 valid OK 32.18s
tc13044-par-valid-08 valid OK 32.11s
tc13044-par-valid-09 valid OK 32.01s
tc13044-par-escal-00 escal HTTP 431 13.57s
tc13044-par-escal-01 escal HTTP 431 14.67s
tc13044-par-escal-02 escal HTTP 431 14.44s
tc13044-par-escal-03 escal HTTP 431 15.35s
tc13044-par-escal-04 escal HTTP 431 14.90s
tc13044-par-escal-05 escal HTTP 431 15.41s
tc13044-par-escal-06 escal HTTP 431 14.06s
tc13044-par-escal-07 escal HTTP 431 14.82s
tc13044-par-escal-08 escal HTTP 431 14.29s
tc13044-par-escal-09 escal HTTP 431 14.46s
mysql> SELECT COUNT(*) AS valid_count FROM account WHERE account_name LIKE 'tc13044-par-valid-%';
+-------------+
| valid_count |
+-------------+
| 10 |
+-------------+
mysql> SELECT COUNT(*) AS escal_count FROM account WHERE account_name LIKE 'tc13044-par-escal-%';
+-------------+
| escal_count |
+-------------+
| 0 |
+-------------+
mysql> SELECT account_name, role_id FROM account WHERE account_name LIKE 'tc13044-par-%' ORDER BY account_name LIMIT 25;
+----------------------+---------+
| account_name | role_id |
+----------------------+---------+
| tc13044-par-valid-00 | 4 |
| tc13044-par-valid-01 | 4 |
| tc13044-par-valid-02 | 4 |
| tc13044-par-valid-03 | 4 |
| tc13044-par-valid-04 | 4 |
| tc13044-par-valid-05 | 4 |
| tc13044-par-valid-06 | 4 |
| tc13044-par-valid-07 | 4 |
| tc13044-par-valid-08 | 4 |
| tc13044-par-valid-09 | 4 |
+----------------------+---------+
**Logs check:**
# grep -iE "deadlock|exception" /var/log/cloudstack/management/management-server.log | grep -i "tc13044-par" | tail -30
(no output)
Actual result:
- All 10 valid creates succeeded with
role_id=4(User role) persisted. - All 10 escalation attempts denied with HTTP 431 and zero orphan rows.
- No deadlock or exception log entries.
- Escalation rejection path here is the early domain-validation check (rejecting Admin role in non-ROOT domain), a different code path than TC10's
checkRoleEscalation. Confirms early validation also remains correct under concurrent load. - PASS — no deadlocks, no orphan rows, mgmt server remained responsive.
TC14: Two rapid sequential creates without UUID - no collision
Description: Confirm that PR 13044's pre-transaction UUID.randomUUID() resolution does not produce duplicate UUIDs when called in rapid succession.
Prerequisites:
- Test domain test-pr13044, default User role
- Caller: root admin
Steps:
- Run two createAccount calls back-to-back without supplying accountid:
create account accounttype=0 username=tc14-rapid-01 ... domainid=415b04d7-... roleid=d6e04f12-...
create account accounttype=0 username=tc14-rapid-02 ... domainid=415b04d7-... roleid=d6e04f12-... - Compare the auto-generated UUIDs.
Expected result:
- Both creates succeed.
- The two
account.idUUIDs are distinct and valid v4 format.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc14-rapid-01 password=Password123 firstname=TC14 lastname=Rapid1 email=tc14-rapid-01@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
{
"account": {
"id": "9f4c731b-8b2b-469a-9871-e5fb038ff251",
"name": "tc14-rapid-01",
"state": "enabled",
...
}
}
(localcloud) 🐱 > create account accounttype=0 username=tc14-rapid-02 password=Password123 firstname=TC14 lastname=Rapid2 email=tc14-rapid-02@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
{
"account": {
"id": "70a0f1c2-da73-45bf-8dda-057ad55e174d",
"name": "tc14-rapid-02",
"state": "enabled",
...
}
}
Actual result:
- Both creates succeeded.
- Distinct UUIDs assigned:
9f4c731b-8b2b-469a-9871-e5fb038ff251and70a0f1c2-da73-45bf-8dda-057ad55e174d. - PR 13044's pre-transaction UUID resolution does not produce collisions on rapid sequential calls.
- PASS
TC15: Full account lifecycle (create, update, disable, delete)
Description: Verify the standard account lifecycle (create → update → disable → delete) still works end to end.
Prerequisites:
- CloudStack 4.22.1.0-shapeblue18497 with PR 13044 applied
- Test domain test-pr13044, default User role
- Caller: root admin
- DB access on management server
Steps:
- Create a User account
tc15-lifecycle. - Update the account, changing networkdomain to
tc15-updated.test.local. - Disable the account.
- Delete the account.
- Verify in DB that the account row has a non-NULL
removedtimestamp (soft delete).
Expected result:
- Each step succeeds.
- After delete, the account row exists in DB but has a
removedtimestamp set.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc15-lifecycle password=Password123 firstname=TC15 lastname=Lifecycle email=tc15@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
{
"account": {
"id": "07e0b7fe-3aac-495b-bba0-d2bca19b33c2",
"name": "tc15-lifecycle",
"state": "enabled",
"rolename": "User",
"roletype": "User",
...
}
}
(localcloud) 🐱 > update account id=07e0b7fe-3aac-495b-bba0-d2bca19b33c2 networkdomain=tc15-updated.test.local
{
"account": {
"id": "07e0b7fe-3aac-495b-bba0-d2bca19b33c2",
"name": "tc15-lifecycle",
"networkdomain": "tc15-updated.test.local",
...
}
}
(localcloud) 🐱 > disable account id=07e0b7fe-3aac-495b-bba0-d2bca19b33c2 lock=false
{
"account": {
"id": "07e0b7fe-3aac-495b-bba0-d2bca19b33c2",
"state": "disabled",
...
}
}
(localcloud) 🐱 > delete account id=07e0b7fe-3aac-495b-bba0-d2bca19b33c2
{
"success": true
}
mysql> SELECT id, account_name, removed FROM account WHERE account_name='tc15-lifecycle';
+----+----------------+---------------------+
| id | account_name | removed |
+----+----------------+---------------------+
| 29 | tc15-lifecycle | 2026-04-27 21:25:14 |
+----+----------------+---------------------+
1 row in set (0.00 sec)
Actual result:
- Create succeeded, account id
07e0b7fe-3aac-495b-bba0-d2bca19b33c2. - Update succeeded,
networkdomainset totc15-updated.test.local. - Disable succeeded, state changed to
disabled. - Delete returned
success: true. - DB confirms soft delete:
removed=2026-04-27 21:25:14. - PASS
TC16: Create account with invalid roleId
Description: Verify that an invalid roleId is rejected with no orphan rows. Confirms that PR 13044's pre-transaction setup does not bypass parameter validation.
Prerequisites:
- Test domain test-pr13044
- Caller: root admin
- DB access on management server
Steps:
- Run createAccount with a non-existent UUID for roleid:
create account accounttype=0 username=tc16-invalid-role ... roleid=00000000-0000-0000-0000-000000000000 - Verify the call is rejected.
- Verify no rows in
accountandusertables for this username.
Expected result:
- Error returned, mentioning the invalid roleid.
- Zero rows in
accountandusertables.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc16-invalid-role password=Password123 firstname=TC16 lastname=InvalidRole email=tc16@test.local domainid=415b04d7-8251-4ee1-ac43-162d4668be52 roleid=00000000-0000-0000-0000-000000000000
🙈 Error: (HTTP 431, error code 9999) Unable to execute API command createaccount due to invalid value. Invalid parameter roleid value=00000000-0000-0000-0000-000000000000 due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.
mysql> SELECT id, account_name FROM account WHERE account_name='tc16-invalid-role';
Empty set (0.00 sec)
mysql> SELECT id, username FROM user WHERE username='tc16-invalid-role';
Empty set (0.00 sec)
Actual result:
- Call rejected with HTTP 431 and a clear error message: "Invalid parameter roleid value=00000000-0000-0000-0000-000000000000 due to incorrect long value format, or entity does not exist".
- Rejection happens at the parameter validation layer, before the createAccount logic and before any DB write.
- Zero orphan rows in
accountandusertables. - PASS
TC17: Create account with invalid domainId
Description: Verify that an invalid domainId is rejected with no orphan rows.
Prerequisites:
- CloudStack 4.22.1.0-shapeblue18497 with PR 13044 applied
- Default User role
- Caller: root admin
- DB access on management server
Steps:
- Run createAccount with a non-existent UUID for domainid:
create account accounttype=0 username=tc17-invalid-domain ... domainid=00000000-0000-0000-0000-000000000000 roleid=d6e04f12-... - Verify the call is rejected.
- Verify no rows in
accountandusertables for this username.
Expected result:
- Error returned, mentioning the invalid domainid.
- Zero rows in
accountandusertables.
Test Evidence:
(localcloud) 🐱 > create account accounttype=0 username=tc17-invalid-domain password=Password123 firstname=TC17 lastname=InvalidDomain email=tc17@test.local domainid=00000000-0000-0000-0000-000000000000 roleid=d6e04f12-4224-11f1-a47c-1e00450001b6
🙈 Error: (HTTP 431, error code 9999) Unable to execute API command createaccount due to invalid value. Invalid parameter domainid value=00000000-0000-0000-0000-000000000000 due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.
mysql> SELECT id, account_name FROM account WHERE account_name='tc17-invalid-domain';
Empty set (0.00 sec)
mysql> SELECT id, username FROM user WHERE username='tc17-invalid-domain';
Empty set (0.00 sec)
Actual result:
- Call rejected with HTTP 431 and a clear error message about the invalid domainid.
- Rejection happens at the parameter validation layer, before any DB write.
- Zero orphan rows.
- PASS
|
@blueorangutan test |
|
@RosiKyu a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
[SF] Trillian test result (tid-15952)
|
|
[SF] Trillian test result (tid-15958)
|
Description
The read-only role escalation check iterates all API commands and does not need a write transaction open. Using a transient AccountVO for the check avoids holding the DB connection during the permission scan, reducing connection pool pressure and API latency.
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?