Merge branch '4.18' into 4.19

This commit is contained in:
nvazquez 2024-08-06 12:29:18 -03:00
commit d541e9014d
No known key found for this signature in database
GPG Key ID: 656E1BCC8CB54F84
6 changed files with 516 additions and 22 deletions

View File

@ -35,6 +35,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
tests: [ "smoke/test_accounts tests: [ "smoke/test_accounts
smoke/test_account_access
smoke/test_affinity_groups smoke/test_affinity_groups
smoke/test_affinity_groups_projects smoke/test_affinity_groups_projects
smoke/test_annotations smoke/test_annotations

View File

@ -216,30 +216,58 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
} else if (entity instanceof AffinityGroup) { } else if (entity instanceof AffinityGroup) {
return false; return false;
} else { } else {
if (_accountService.isNormalUser(caller.getId())) { validateCallerHasAccessToEntityOwner(caller, entity, accessType);
Account account = _accountDao.findById(entity.getAccountId());
String errorMessage = String.format("%s does not have permission to operate with resource", caller);
if (account != null && account.getType() == Account.Type.PROJECT) {
//only project owner can delete/modify the project
if (accessType != null && accessType == AccessType.ModifyProject) {
if (!_projectMgr.canModifyProjectAccount(caller, account.getId())) {
throw new PermissionDeniedException(errorMessage);
}
} else if (!_projectMgr.canAccessProjectAccount(caller, account.getId())) {
throw new PermissionDeniedException(errorMessage);
}
checkOperationPermitted(caller, entity);
} else {
if (caller.getId() != entity.getAccountId()) {
throw new PermissionDeniedException(errorMessage);
}
}
}
} }
return true; return true;
} }
private boolean checkOperationPermitted(Account caller, ControlledEntity entity) { protected void validateCallerHasAccessToEntityOwner(Account caller, ControlledEntity entity, AccessType accessType) {
PermissionDeniedException exception = new PermissionDeniedException("Caller does not have permission to operate with provided resource.");
String entityLog = String.format("entity [owner ID: %d, type: %s]", entity.getAccountId(),
entity.getEntityType().getSimpleName());
if (_accountService.isRootAdmin(caller.getId())) {
return;
}
if (caller.getId() == entity.getAccountId()) {
return;
}
Account owner = _accountDao.findById(entity.getAccountId());
if (owner == null) {
s_logger.error(String.format("Owner not found for %s", entityLog));
throw exception;
}
Account.Type callerAccountType = caller.getType();
if ((callerAccountType == Account.Type.DOMAIN_ADMIN || callerAccountType == Account.Type.RESOURCE_DOMAIN_ADMIN) &&
_domainDao.isChildDomain(caller.getDomainId(), owner.getDomainId())) {
return;
}
if (owner.getType() == Account.Type.PROJECT) {
// only project owner can delete/modify the project
if (accessType == AccessType.ModifyProject) {
if (!_projectMgr.canModifyProjectAccount(caller, owner.getId())) {
s_logger.error(String.format("Caller ID: %d does not have permission to modify project with " +
"owner ID: %d", caller.getId(), owner.getId()));
throw exception;
}
} else if (!_projectMgr.canAccessProjectAccount(caller, owner.getId())) {
s_logger.error(String.format("Caller ID: %d does not have permission to access project with " +
"owner ID: %d", caller.getId(), owner.getId()));
throw exception;
}
checkOperationPermitted(caller, entity);
return;
}
s_logger.error(String.format("Caller ID: %d does not have permission to access %s", caller.getId(), entityLog));
throw exception;
}
protected boolean checkOperationPermitted(Account caller, ControlledEntity entity) {
User user = CallContext.current().getCallingUser(); User user = CallContext.current().getCallingUser();
Project project = projectDao.findByProjectAccountId(entity.getAccountId()); Project project = projectDao.findByProjectAccountId(entity.getAccountId());
if (project == null) { if (project == null) {

View File

@ -2744,7 +2744,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
throw new InvalidParameterValueException("Unable to find user by id"); throw new InvalidParameterValueException("Unable to find user by id");
} }
final ControlledEntity account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user. final ControlledEntity account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user.
checkAccess(CallContext.current().getCallingUser(), account); User caller = CallContext.current().getCallingUser();
preventRootDomainAdminAccessToRootAdminKeys(caller, account);
checkAccess(caller, account);
Map<String, String> keys = new HashMap<String, String>(); Map<String, String> keys = new HashMap<String, String>();
keys.put("apikey", user.getApiKey()); keys.put("apikey", user.getApiKey());
@ -2753,6 +2755,19 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
return keys; return keys;
} }
protected void preventRootDomainAdminAccessToRootAdminKeys(User caller, ControlledEntity account) {
if (isDomainAdminForRootDomain(caller) && isRootAdmin(account.getAccountId())) {
String msg = String.format("Caller Username %s does not have access to root admin keys", caller.getUsername());
s_logger.error(msg);
throw new PermissionDeniedException(msg);
}
}
protected boolean isDomainAdminForRootDomain(User callingUser) {
AccountVO caller = _accountDao.findById(callingUser.getAccountId());
return caller.getType() == Account.Type.DOMAIN_ADMIN && caller.getDomainId() == Domain.ROOT_DOMAIN;
}
@Override @Override
public List<UserTwoFactorAuthenticator> listUserTwoFactorAuthenticationProviders() { public List<UserTwoFactorAuthenticator> listUserTwoFactorAuthenticationProviders() {
return userTwoFactorAuthenticationProviders; return userTwoFactorAuthenticationProviders;
@ -2787,6 +2802,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
} }
Account account = _accountDao.findById(user.getAccountId()); Account account = _accountDao.findById(user.getAccountId());
preventRootDomainAdminAccessToRootAdminKeys(user, account);
checkAccess(caller, null, true, account); checkAccess(caller, null, true, account);
// don't allow updating system user // don't allow updating system user

View File

@ -0,0 +1,166 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.acl;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.projects.ProjectManager;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.Ternary;
@RunWith(MockitoJUnitRunner.class)
public class DomainCheckerTest {
@Mock
AccountService _accountService;
@Mock
AccountDao _accountDao;
@Mock
DomainDao _domainDao;
@Mock
ProjectManager _projectMgr;
@Spy
@InjectMocks
DomainChecker domainChecker;
private ControlledEntity getMockedEntity(long accountId) {
ControlledEntity entity = Mockito.mock(Account.class);
Mockito.when(entity.getAccountId()).thenReturn(accountId);
Mockito.when(entity.getEntityType()).thenReturn((Class)Account.class);
return entity;
}
@Test
public void testRootAdminHasAccess() {
Account rootAdmin = Mockito.mock(Account.class);
Mockito.when(rootAdmin.getId()).thenReturn(1L);
ControlledEntity entity = getMockedEntity(2L);
Mockito.when(_accountService.isRootAdmin(rootAdmin.getId())).thenReturn(true);
domainChecker.validateCallerHasAccessToEntityOwner(rootAdmin, entity, SecurityChecker.AccessType.ModifyProject);
}
@Test
public void testCallerIsOwner() {
Account caller = Mockito.mock(Account.class);
Mockito.when(caller.getId()).thenReturn(1L);
ControlledEntity entity = getMockedEntity(1L);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ModifyProject);
}
@Test(expected = PermissionDeniedException.class)
public void testOwnerNotFound() {
Account caller = Mockito.mock(Account.class);
Mockito.when(caller.getId()).thenReturn(1L);
ControlledEntity entity = getMockedEntity(2L);
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(null);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ModifyProject);
}
@Test
public void testDomainAdminHasAccess() {
Account caller = Mockito.mock(Account.class);
Mockito.when(caller.getId()).thenReturn(1L);
Mockito.when(caller.getDomainId()).thenReturn(100L);
Mockito.when(caller.getType()).thenReturn(Account.Type.DOMAIN_ADMIN);
ControlledEntity entity = getMockedEntity(2L);
AccountVO owner = Mockito.mock(AccountVO.class);
Mockito.when(owner.getDomainId()).thenReturn(101L);
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(owner);
Mockito.when(_domainDao.isChildDomain(100L, 101L)).thenReturn(true);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ModifyProject);
}
private Ternary<Account, ControlledEntity, AccountVO> getProjectAccessCheckResources() {
Account caller = Mockito.mock(Account.class);
Mockito.when(caller.getId()).thenReturn(100L);
Mockito.when(caller.getType()).thenReturn(Account.Type.PROJECT);
ControlledEntity entity = getMockedEntity(2L);
AccountVO projectAccount = Mockito.mock(AccountVO.class);
Mockito.when(projectAccount.getId()).thenReturn(2L);
Mockito.when(projectAccount.getType()).thenReturn(Account.Type.PROJECT);
return new Ternary<>(caller, entity, projectAccount);
}
@Test
public void testProjectOwnerCanModify() {
Ternary<Account, ControlledEntity, AccountVO> resources = getProjectAccessCheckResources();
Account caller = resources.first();
ControlledEntity entity = resources.second();
AccountVO projectAccount = resources.third();
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(projectAccount);
Mockito.when(_projectMgr.canModifyProjectAccount(caller, projectAccount.getId())).thenReturn(true);
Mockito.doReturn(true).when(domainChecker).checkOperationPermitted(caller, entity);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ModifyProject);
}
@Test(expected = PermissionDeniedException.class)
public void testProjectOwnerCannotModify() {
Ternary<Account, ControlledEntity, AccountVO> resources = getProjectAccessCheckResources();
Account caller = resources.first();
ControlledEntity entity = resources.second();
AccountVO projectAccount = resources.third();
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(projectAccount);
Mockito.when(_projectMgr.canModifyProjectAccount(caller, projectAccount.getId())).thenReturn(false);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ModifyProject);
}
@Test
public void testProjectOwnerCanAccess() {
Ternary<Account, ControlledEntity, AccountVO> resources = getProjectAccessCheckResources();
Account caller = resources.first();
ControlledEntity entity = resources.second();
AccountVO projectAccount = resources.third();
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(projectAccount);
Mockito.when(_projectMgr.canAccessProjectAccount(caller, projectAccount.getId())).thenReturn(true);
Mockito.doReturn(true).when(domainChecker).checkOperationPermitted(caller, entity);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ListEntry);
}
@Test(expected = PermissionDeniedException.class)
public void testProjectOwnerCannotAccess() {
Ternary<Account, ControlledEntity, AccountVO> resources = getProjectAccessCheckResources();
Account caller = resources.first();
ControlledEntity entity = resources.second();
AccountVO projectAccount = resources.third();
Mockito.when(_accountDao.findById(entity.getAccountId())).thenReturn(projectAccount);
Mockito.when(_projectMgr.canAccessProjectAccount(caller, projectAccount.getId())).thenReturn(false);
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ListEntry);
}
}

View File

@ -16,6 +16,34 @@
// under the License. // under the License.
package com.cloud.user; package com.cloud.user;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
import org.apache.cloudstack.auth.UserAuthenticator;
import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication;
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.ArgumentMatchers.nullable;
import com.cloud.acl.DomainChecker; import com.cloud.acl.DomainChecker;
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd; import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
import com.cloud.domain.Domain; import com.cloud.domain.Domain;
@ -241,6 +269,63 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
accountManagerImpl.getKeys(_listkeyscmd); accountManagerImpl.getKeys(_listkeyscmd);
} }
@Test(expected = PermissionDeniedException.class)
public void testGetUserKeysCmdDomainAdminRootAdminUser() {
CallContext.register(callingUser, callingAccount);
Mockito.when(_listkeyscmd.getID()).thenReturn(2L);
Mockito.when(accountManagerImpl.getActiveUser(2L)).thenReturn(userVoMock);
Mockito.when(userAccountDaoMock.findById(2L)).thenReturn(userAccountVO);
Mockito.when(userAccountVO.getAccountId()).thenReturn(2L);
Mockito.when(userDetailsDaoMock.listDetailsKeyPairs(Mockito.anyLong())).thenReturn(null);
// Queried account - admin account
AccountVO adminAccountMock = Mockito.mock(AccountVO.class);
Mockito.when(adminAccountMock.getAccountId()).thenReturn(2L);
Mockito.when(_accountDao.findByIdIncludingRemoved(2L)).thenReturn(adminAccountMock);
Mockito.lenient().when(accountService.isRootAdmin(2L)).thenReturn(true);
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class),
Mockito.nullable(ControlledEntity.class), Mockito.nullable(AccessType.class), Mockito.anyString())).thenReturn(true);
// Calling account is domain admin of the ROOT domain
Mockito.lenient().when(callingAccount.getType()).thenReturn(Account.Type.DOMAIN_ADMIN);
Mockito.lenient().when(callingAccount.getDomainId()).thenReturn(Domain.ROOT_DOMAIN);
Mockito.lenient().when(callingUser.getAccountId()).thenReturn(2L);
Mockito.lenient().when(_accountDao.findById(2L)).thenReturn(callingAccount);
Mockito.lenient().when(accountService.isDomainAdmin(Mockito.anyLong())).thenReturn(Boolean.TRUE);
Mockito.lenient().when(accountMock.getAccountId()).thenReturn(2L);
accountManagerImpl.getKeys(_listkeyscmd);
}
@Test
public void testPreventRootDomainAdminAccessToRootAdminKeysNormalUser() {
User user = Mockito.mock(User.class);
ControlledEntity entity = Mockito.mock(ControlledEntity.class);
Mockito.when(user.getAccountId()).thenReturn(1L);
AccountVO account = Mockito.mock(AccountVO.class);
Mockito.when(account.getType()).thenReturn(Account.Type.NORMAL);
Mockito.when(_accountDao.findById(1L)).thenReturn(account);
accountManagerImpl.preventRootDomainAdminAccessToRootAdminKeys(user, entity);
Mockito.verify(accountManagerImpl, Mockito.never()).isRootAdmin(Mockito.anyLong());
}
@Test(expected = PermissionDeniedException.class)
public void testPreventRootDomainAdminAccessToRootAdminKeysRootDomainAdminUser() {
User user = Mockito.mock(User.class);
ControlledEntity entity = Mockito.mock(ControlledEntity.class);
Mockito.when(user.getAccountId()).thenReturn(1L);
AccountVO account = Mockito.mock(AccountVO.class);
Mockito.when(account.getType()).thenReturn(Account.Type.DOMAIN_ADMIN);
Mockito.when(account.getDomainId()).thenReturn(Domain.ROOT_DOMAIN);
Mockito.when(_accountDao.findById(1L)).thenReturn(account);
Mockito.when(entity.getAccountId()).thenReturn(1L);
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class),
Mockito.nullable(ControlledEntity.class), Mockito.nullable(AccessType.class), Mockito.anyString())).thenReturn(true);
accountManagerImpl.preventRootDomainAdminAccessToRootAdminKeys(user, entity);
}
@Test @Test
public void updateUserTestTimeZoneAndEmailNull() { public void updateUserTestTimeZoneAndEmailNull() {
prepareMockAndExecuteUpdateUserTest(0); prepareMockAndExecuteUpdateUserTest(0);

View File

@ -0,0 +1,198 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
""" BVT tests for Account User Access
"""
# Import Local Modules
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.utils import *
from marvin.lib.base import (Account,
User,
Domain)
from marvin.lib.common import (get_domain)
from marvin.cloudstackAPI import (getUserKeys)
from marvin.cloudstackException import CloudstackAPIException
from nose.plugins.attrib import attr
_multiprocess_shared_ = True
class TestAccountAccess(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestAccountAccess, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
cls.hypervisor = testClient.getHypervisorInfo()
cls._cleanup = []
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.domains = []
cls.domain_admins = {}
cls.domain_users = {}
cls.account_users = {}
domain_data = {
"name": "domain_1"
}
cls.domain_1 = Domain.create(
cls.apiclient,
domain_data,
)
cls._cleanup.append(cls.domain_1)
cls.domains.append(cls.domain_1)
domain_data["name"] = "domain_11"
cls.domain_11 = Domain.create(
cls.apiclient,
domain_data,
parentdomainid=cls.domain_1.id
)
cls._cleanup.append(cls.domain_11)
cls.domains.append(cls.domain_11)
domain_data["name"] = "domain_12"
cls.domain_12 = Domain.create(
cls.apiclient,
domain_data,
parentdomainid=cls.domain_1.id
)
cls._cleanup.append(cls.domain_12)
cls.domains.append(cls.domain_12)
domain_data["name"] = "domain_2"
cls.domain_2 = Domain.create(
cls.apiclient,
domain_data,
)
cls._cleanup.append(cls.domain_2)
cls.domains.append(cls.domain_2)
for d in cls.domains:
cls.create_domainadmin_and_user(d)
@classmethod
def tearDownClass(cls):
super(TestAccountAccess, cls).tearDownClass()
@classmethod
def create_account(cls, domain, is_admin):
cls.debug(f"Creating account for domain {domain.name}, admin: {is_admin}")
data = {
"email": "admin-" + domain.name + "@test.com",
"firstname": "Admin",
"lastname": domain.name,
"username": "admin-" + domain.name,
"password": "password"
}
if is_admin == False:
data["email"] = "user-" + domain.name + "@test.com"
data["firstname"] = "User"
data["username"] = "user-" + domain.name
account = Account.create(
cls.apiclient,
data,
admin=is_admin,
domainid=domain.id
)
cls._cleanup.append(account)
if is_admin == True:
cls.domain_admins[domain.id] = account
else:
cls.domain_users[domain.id] = account
user = User.create(
cls.apiclient,
data,
account=account.name,
domainid=account.domainid)
cls._cleanup.append(user)
cls.account_users[account.id] = user
@classmethod
def create_domainadmin_and_user(cls, domain):
cls.debug(f"Creating accounts for domain #{domain.id} {domain.name}")
cls.create_account(domain, True)
cls.create_account(domain, False)
def get_user_keys(self, api_client, user_id):
getUserKeysCmd = getUserKeys.getUserKeysCmd()
getUserKeysCmd.id = user_id
return api_client.getUserKeys(getUserKeysCmd)
def is_child_domain(self, parent_domain, child_domain):
if not parent_domain or not child_domain:
return False
parent_domain_prefix = parent_domain.split('-')[0]
child_domain_prefix = child_domain.split('-')[0]
if not parent_domain_prefix or not child_domain_prefix:
return False
return child_domain_prefix.startswith(parent_domain_prefix)
@attr(tags=["advanced", "advancedns", "smoke", "sg"], required_hardware="false")
def test_01_user_access(self):
"""
Test user account is not accessing any other account
"""
domain_user_accounts = [value for value in self.domain_users.values()]
all_account_users = [value for value in self.account_users.values()]
for user_account in domain_user_accounts:
current_account_user = self.account_users[user_account.id]
self.debug(f"Check for account {user_account.name} with user {current_account_user.username}")
user_api_client = self.testClient.getUserApiClient(
UserName=user_account.name,
DomainName=user_account.domain
)
for user in all_account_users:
self.debug(f"Checking access for user {user.username} associated with account {user.account}")
try:
self.get_user_keys(user_api_client, user.id)
self.debug(f"API successful")
if user.id != current_account_user.id:
self.fail(f"User account #{user_account.id} was able to access another account #{user.id}")
except CloudstackAPIException as e:
self.debug(f"Exception occurred: {e}")
if user.id == current_account_user.id:
self.fail(f"User account #{user_account.id} not able to access own account")
@attr(tags=["advanced", "advancedns", "smoke", "sg"], required_hardware="false")
def test_02_domain_admin_access(self):
"""
Test domain admin account is not accessing any other account from unauthorized domain
"""
domain_admin_accounts = [value for value in self.domain_admins.values()]
all_account_users = [value for value in self.account_users.values()]
for admin_account in domain_admin_accounts:
current_account_user = self.account_users[admin_account.id]
self.debug(f"Check for domain admin {admin_account.name} with user {current_account_user.username}, {current_account_user.domain}")
admin_api_client = self.testClient.getUserApiClient(
UserName=admin_account.name,
DomainName=admin_account.domain
)
for user in all_account_users:
self.debug(f"Checking access for user {user.username}, {user.domain} associated with account {user.account}")
try:
self.get_user_keys(admin_api_client, user.id)
self.debug(f"API successful")
if self.is_child_domain(current_account_user.domain, user.domain) == False:
self.fail(f"User account #{admin_account.id} was able to access another account #{user.id}")
except CloudstackAPIException as e:
self.debug(f"Exception occurred: {e}")
if self.is_child_domain(current_account_user.domain, user.domain) == True:
self.fail(f"User account #{admin_account.id} not able to access own account")