Global ACL for VPCs (#7150)

This commit is contained in:
Bryan Lima 2023-11-30 10:51:43 -03:00 committed by GitHub
parent 26b01f6f3b
commit cb62ce6767
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 494 additions and 114 deletions

View File

@ -111,7 +111,8 @@ jobs:
smoke/test_reset_configuration_settings
smoke/test_reset_vm_on_reboot
smoke/test_resource_accounting
smoke/test_resource_detail",
smoke/test_resource_detail
smoke/test_global_acls",
"smoke/test_router_dhcphosts
smoke/test_router_dns
smoke/test_router_dnsservice

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.network;
import com.cloud.exception.PermissionDeniedException;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
@ -26,6 +27,7 @@ import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.NetworkACLResponse;
import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import com.cloud.event.EventTypes;
@ -35,7 +37,8 @@ import com.cloud.network.vpc.NetworkACL;
import com.cloud.network.vpc.Vpc;
import com.cloud.user.Account;
@APICommand(name = "createNetworkACLList", description = "Creates a network ACL for the given VPC", responseObject = NetworkACLResponse.class,
@APICommand(name = "createNetworkACLList", description = "Creates a network ACL. If no VPC is given, then it creates a global ACL that can be used by everyone.",
responseObject = NetworkACLResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
public static final Logger s_logger = Logger.getLogger(CreateNetworkACLListCmd.class.getName());
@ -53,7 +56,6 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
@Parameter(name = ApiConstants.VPC_ID,
type = CommandType.UUID,
required = true,
entityType = VpcResponse.class,
description = "ID of the VPC associated with this network ACL list")
private Long vpcId;
@ -77,6 +79,10 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
return vpcId;
}
public void setVpcId(Long vpcId) {
this.vpcId = vpcId;
}
@Override
public boolean isDisplay() {
if (display != null) {
@ -92,6 +98,9 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
@Override
public void create() {
if (getVpcId() == null) {
setVpcId(0L);
}
NetworkACL result = _networkACLService.createNetworkACL(getName(), getDescription(), getVpcId(), isDisplay());
setEntityId(result.getId());
setEntityUuid(result.getUuid());
@ -111,12 +120,21 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
@Override
public long getEntityOwnerId() {
Vpc vpc = _entityMgr.findById(Vpc.class, getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException("Invalid vpcId is given");
}
Account account;
if (isAclAttachedToVpc(this.vpcId)) {
Vpc vpc = _entityMgr.findById(Vpc.class, this.vpcId);
if (vpc == null) {
throw new InvalidParameterValueException(String.format("Invalid VPC ID [%s] provided.", this.vpcId));
}
account = _accountService.getAccount(vpc.getAccountId());
} else {
account = CallContext.current().getCallingAccount();
if (!Account.Type.ADMIN.equals(account.getType())) {
s_logger.warn(String.format("Only Root Admin can create global ACLs. Account [%s] cannot create any global ACL.", account));
throw new PermissionDeniedException("Only Root Admin can create global ACLs.");
}
Account account = _accountService.getAccount(vpc.getAccountId());
}
return account.getId();
}
@ -139,4 +157,8 @@ public class CreateNetworkACLListCmd extends BaseAsyncCreateCmd {
public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.NetworkAcl;
}
public boolean isAclAttachedToVpc(Long aclVpcId) {
return aclVpcId != null && aclVpcId != 0;
}
}

View File

@ -17,6 +17,8 @@
package com.cloud.network.vpc;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import java.util.UUID;
import javax.persistence.Column;
@ -85,6 +87,11 @@ public class NetworkACLVO implements NetworkACL {
return name;
}
@Override
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "vpcId");
}
public void setUuid(String uuid) {
this.uuid = uuid;
}

View File

@ -2100,12 +2100,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
throw new InvalidParameterValueException("Unable to find specified NetworkACL");
}
if (aclId != NetworkACL.DEFAULT_DENY && aclId != NetworkACL.DEFAULT_ALLOW) {
// ACL is not default DENY/ALLOW
// ACL should be associated with a VPC
if (!vpcId.equals(acl.getVpcId())) {
throw new InvalidParameterValueException("ACL: " + aclId + " do not belong to the VPC");
}
Long aclVpcId = acl.getVpcId();
if (!isDefaultAcl(aclId) && isAclAttachedToVpc(aclVpcId, vpcId)) {
throw new InvalidParameterValueException(String.format("ACL [%s] does not belong to the VPC [%s].", aclId, aclVpcId));
}
}
network = _vpcMgr.createVpcGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType,
@ -5950,6 +5947,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
return new ConfigKey<?>[] {AllowDuplicateNetworkName, AllowEmptyStartEndIpAddress, VRPrivateInterfaceMtu, VRPublicInterfaceMtu, AllowUsersToSpecifyVRMtu};
}
public boolean isDefaultAcl(Long aclId) {
return aclId == NetworkACL.DEFAULT_DENY || aclId == NetworkACL.DEFAULT_ALLOW;
}
public boolean isAclAttachedToVpc(Long aclVpcId, Long vpcId) {
return aclVpcId != 0 && !vpcId.equals(aclVpcId);
}
@Override
public PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd) throws CloudRuntimeException {
Long ipId = cmd.getId();

View File

@ -24,6 +24,7 @@ import java.util.Map;
import javax.inject.Inject;
import com.cloud.exception.PermissionDeniedException;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
@ -103,12 +104,15 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
@Override
public NetworkACL createNetworkACL(final String name, final String description, final long vpcId, final Boolean forDisplay) {
final Account caller = CallContext.current().getCallingAccount();
final Vpc vpc = _entityMgr.findById(Vpc.class, vpcId);
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find VPC");
if (vpcId != 0) {
final Account caller = CallContext.current().getCallingAccount();
final Vpc vpc = _vpcDao.findById(vpcId);
if (vpc == null) {
throw new InvalidParameterValueException(String.format("Unable to find VPC with ID [%s].", vpcId));
}
_accountMgr.checkAccess(caller, null, true, vpc);
}
_accountMgr.checkAccess(caller, null, true, vpc);
return _networkAclMgr.createNetworkACL(name, description, vpcId, forDisplay);
}
@ -212,22 +216,17 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
@Override
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_ACL_DELETE, eventDescription = "Deleting Network ACL List", async = true)
public boolean deleteNetworkACL(final long id) {
final Account caller = CallContext.current().getCallingAccount();
final NetworkACL acl = _networkACLDao.findById(id);
Account account = CallContext.current().getCallingAccount();
if (acl == null) {
throw new InvalidParameterValueException("Unable to find specified ACL");
}
//Do not allow deletion of default ACLs
if (acl.getId() == NetworkACL.DEFAULT_ALLOW || acl.getId() == NetworkACL.DEFAULT_DENY) {
if (isDefaultAcl(acl.getId())) {
throw new InvalidParameterValueException("Default ACL cannot be removed");
}
final Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find specified VPC associated with the ACL");
}
_accountMgr.checkAccess(caller, null, true, vpc);
validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account, "Only Root Admin can delete global ACLs.");
return _networkAclMgr.deleteNetworkACL(acl);
}
@ -253,7 +252,7 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
throw new InvalidParameterValueException("Unable to find specified vpc id");
}
if (aclId != NetworkACL.DEFAULT_DENY && aclId != NetworkACL.DEFAULT_ALLOW) {
if (!isDefaultAcl(aclId)) {
final Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find Vpc associated with the NetworkACL");
@ -293,15 +292,9 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
throw new InvalidParameterValueException("Network ACL can be created just for networks of type " + Networks.TrafficType.Guest);
}
if (aclId != NetworkACL.DEFAULT_DENY && aclId != NetworkACL.DEFAULT_ALLOW) {
//ACL is not default DENY/ALLOW
// ACL should be associated with a VPC
final Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find Vpc associated with the NetworkACL");
}
if (!isDefaultAcl(aclId) && !isGlobalAcl(acl.getVpcId())) {
validateAclAssociatedToVpc(acl.getVpcId(), caller, acl.getUuid());
_accountMgr.checkAccess(caller, null, true, vpc);
if (!network.getVpcId().equals(acl.getVpcId())) {
throw new InvalidParameterValueException("Network: " + networkId + " and ACL: " + aclId + " do not belong to the same VPC");
}
@ -340,6 +333,11 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
NetworkACL acl = _networkAclMgr.getNetworkACL(aclId);
validateNetworkAcl(acl);
Account caller = CallContext.current().getCallingAccount();
if (isGlobalAcl(acl.getVpcId()) && !Account.Type.ADMIN.equals(caller.getType())) {
throw new PermissionDeniedException("Only Root Admins can create rules for a global ACL.");
}
validateAclRuleNumber(createNetworkACLCmd, acl);
NetworkACLItem.Action ruleAction = validateAndCreateNetworkAclRuleAction(action);
@ -409,7 +407,8 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
* <ul>
* <li>If the parameter is null, we return an {@link InvalidParameterValueException};
* <li>Default ACLs {@link NetworkACL#DEFAULT_ALLOW} and {@link NetworkACL#DEFAULT_DENY} cannot be modified. Therefore, if any of them is provided we throw a {@link InvalidParameterValueException};
* <li>If the network does not have a VPC, we will throw an {@link InvalidParameterValueException}.
* <li>If no VPC is given, then it is a global ACL and there is no need to check any VPC ID. However, if a VPC is given and it does not exist, throws an
* {@link InvalidParameterValueException}.
* </ul>
*
* After all validations, we check if the user has access to the given network ACL using {@link AccountManager#checkAccess(Account, org.apache.cloudstack.acl.SecurityChecker.AccessType, boolean, org.apache.cloudstack.acl.ControlledEntity...)}.
@ -419,16 +418,14 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
throw new InvalidParameterValueException("Unable to find specified ACL.");
}
if (acl.getId() == NetworkACL.DEFAULT_DENY || acl.getId() == NetworkACL.DEFAULT_ALLOW) {
if (isDefaultAcl(acl.getId())) {
throw new InvalidParameterValueException("Default ACL cannot be modified");
}
Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException(String.format("Unable to find Vpc associated with the NetworkACL [%s]", acl.getUuid()));
Long aclVpcId = acl.getVpcId();
if (!isGlobalAcl(aclVpcId)) {
validateAclAssociatedToVpc(aclVpcId, CallContext.current().getCallingAccount(), acl.getUuid());
}
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, vpc);
}
/**
@ -792,17 +789,12 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
final NetworkACLItemVO aclItem = _networkACLItemDao.findById(ruleId);
if (aclItem != null) {
final NetworkACL acl = _networkAclMgr.getNetworkACL(aclItem.getAclId());
final Account account = CallContext.current().getCallingAccount();
final Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (aclItem.getAclId() == NetworkACL.DEFAULT_ALLOW || aclItem.getAclId() == NetworkACL.DEFAULT_DENY) {
if (isDefaultAcl(aclItem.getAclId())) {
throw new InvalidParameterValueException("ACL Items in default ACL cannot be deleted");
}
final Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, vpc);
validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account, "Only Root Admin can delete global ACL rules.");
}
return _networkAclMgr.revokeNetworkACLItem(ruleId);
}
@ -826,6 +818,9 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
NetworkACL acl = _networkAclMgr.getNetworkACL(networkACLItemVo.getAclId());
validateNetworkAcl(acl);
Account account = CallContext.current().getCallingAccount();
validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account, "Only Root Admins can update global ACLs.");
transferDataToNetworkAclRulePojo(updateNetworkACLItemCmd, networkACLItemVo, acl);
validateNetworkACLItem(networkACLItemVo);
return _networkAclMgr.updateNetworkACLItem(networkACLItemVo);
@ -912,14 +907,13 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_ACL_UPDATE, eventDescription = "updating network acl", async = true)
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_ACL_UPDATE, eventDescription = "Updating Network ACL", async = true)
public NetworkACL updateNetworkACL(UpdateNetworkACLListCmd updateNetworkACLListCmd) {
Long id = updateNetworkACLListCmd.getId();
NetworkACLVO acl = _networkACLDao.findById(id);
Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
Account account = CallContext.current().getCallingAccount();
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, vpc);
validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account, "Must be Root Admin to update a global ACL.");
String name = updateNetworkACLListCmd.getName();
if (StringUtils.isNotBlank(name)) {
@ -1149,14 +1143,59 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
long aclId = ruleBeingMoved.getAclId();
if ((nextRule != null && nextRule.getAclId() != aclId) || (previousRule != null && previousRule.getAclId() != aclId)) {
throw new InvalidParameterValueException("Cannot use ACL rules from differenting ACLs. Rule being moved.");
throw new InvalidParameterValueException("Cannot use ACL rules from differentiating ACLs. Rule being moved.");
}
NetworkACLVO acl = _networkACLDao.findById(aclId);
Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId());
if (vpc == null) {
throw new InvalidParameterValueException("Re-ordering rules for a default ACL is prohibited");
Account account = CallContext.current().getCallingAccount();
if (isDefaultAcl(aclId)) {
throw new InvalidParameterValueException("Default ACL rules cannot be moved.");
}
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, vpc);
validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account,"Must be Root Admin to move global ACL rules.");
}
/**
* Checks if the given ACL is a global ACL. If it is, then checks if the account is Root Admin, and throws an exception according to {@code exceptionMessage} param if it
* does not have permission.
*/
protected void checkGlobalAclPermission(Long aclVpcId, Account account, String exceptionMessage) {
if (isGlobalAcl(aclVpcId) && !Account.Type.ADMIN.equals(account.getType())) {
throw new PermissionDeniedException(exceptionMessage);
}
}
protected void validateAclAssociatedToVpc(Long aclVpcId, Account account, String aclUuid) {
final Vpc vpc = _vpcDao.findById(aclVpcId);
if (vpc == null) {
throw new InvalidParameterValueException(String.format("Unable to find specified VPC [%s] associated with the ACL [%s].", aclVpcId, aclUuid));
}
_accountMgr.checkAccess(account, null, true, vpc);
}
/**
* It performs two different verifications depending on if the ACL is global or not.
* <ul>
* <li> If the given ACL is a Global ACL, i.e. has VPC ID = 0, then checks if the account is Root Admin, and throws an exception if it isn't.
* <li> If the given ACL is associated to a VPC, i.e. VPC ID != 0, then calls {@link #validateAclAssociatedToVpc(Long, Account, String)} and checks if the given {@code
* aclVpcId} is from a valid VPC.
* </ul>
*/
protected void validateGlobalAclPermissionAndAclAssociatedToVpc(NetworkACL acl, Account account, String exception){
if (isGlobalAcl(acl.getVpcId())) {
s_logger.info(String.format("Checking if account [%s] has permission to manipulate global ACL [%s].", account, acl));
checkGlobalAclPermission(acl.getVpcId(), account, exception);
} else {
s_logger.info(String.format("Validating ACL [%s] associated to VPC [%s] with account [%s].", acl, acl.getVpcId(), account));
validateAclAssociatedToVpc(acl.getVpcId(), account, acl.getUuid());
}
}
protected boolean isGlobalAcl(Long aclVpcId) {
return aclVpcId != null && aclVpcId == 0;
}
protected boolean isDefaultAcl(long aclId) {
return aclId == NetworkACL.DEFAULT_ALLOW || aclId == NetworkACL.DEFAULT_DENY;
}
}

View File

@ -1977,16 +1977,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
searchBuilder.and("vpcId", searchBuilder.entity().getVpcId(), Op.IN);
final SearchCriteria<NetworkACLVO> searchCriteria = searchBuilder.create();
searchCriteria.setParameters("vpcId", vpcId, 0);
searchCriteria.setParameters("vpcId", vpcId);
final Filter filter = new Filter(NetworkACLVO.class, "id", false, null, null);
final Pair<List<NetworkACLVO>, Integer> aclsCountPair = _networkAclDao.searchAndCount(searchCriteria, filter);
final List<NetworkACLVO> acls = aclsCountPair.first();
for (final NetworkACLVO networkAcl : acls) {
if (networkAcl.getId() != NetworkACL.DEFAULT_ALLOW && networkAcl.getId() != NetworkACL.DEFAULT_DENY) {
_networkAclMgr.deleteNetworkACL(networkAcl);
}
_networkAclMgr.deleteNetworkACL(networkAcl);
}
VpcVO vpc = vpcDao.findById(vpcId);
@ -3175,4 +3173,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
}
return filteredDomainIds;
}
protected boolean isGlobalAcl(Long aclVpcId) {
return aclVpcId != null && aclVpcId == 0;
}
protected boolean isDefaultAcl(long aclId) {
return aclId == NetworkACL.DEFAULT_ALLOW || aclId == NetworkACL.DEFAULT_DENY;
}
}

View File

@ -17,6 +17,40 @@
package com.cloud.network.vpc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.times;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.network.vpc.dao.VpcDao;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLItemCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLListCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
@ -30,40 +64,9 @@ import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLItemCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLListCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.class)
public class NetworkACLServiceImplTest {
@ -80,6 +83,8 @@ public class NetworkACLServiceImplTest {
@Mock
private EntityManager entityManagerMock;
@Mock
private VpcDao vpcDaoMock;
@Mock
private AccountManager accountManagerMock;
@Mock
private NetworkACLDao networkAclDaoMock;
@ -99,10 +104,11 @@ public class NetworkACLServiceImplTest {
@Mock
private UpdateNetworkACLListCmd updateNetworkACLListCmdMock;
private Long networkAclMockId = 1L;
private Long networkAclMockId = 5L;
private Long networkOfferingMockId = 2L;
private Long networkMockVpcMockId = 3L;
private long networkAclListId = 1l;
private static final String SOME_UUID = "someUuid";
@Mock
private MoveNetworkAclItemCmd moveNetworkAclItemCmdMock;
@ -121,6 +127,12 @@ public class NetworkACLServiceImplTest {
@Mock
private CallContext callContextMock;
@Mock
private VpcVO vpcVOMock;
@Mock
private Account accountMock;
private MockedStatic<CallContext> callContextMocked;
@Before
@ -178,9 +190,9 @@ public class NetworkACLServiceImplTest {
}
});
NetworkACLItem netowkrAclRuleCreated = networkAclServiceImpl.createNetworkACLItem(createNetworkAclCmdMock);
NetworkACLItem networkAclRuleCreated = networkAclServiceImpl.createNetworkACLItem(createNetworkAclCmdMock);
Assert.assertEquals(number == null ? 6 : number, netowkrAclRuleCreated.getNumber());
Assert.assertEquals(number == null ? 6 : number, networkAclRuleCreated.getNumber());
InOrder inOrder = Mockito.inOrder( networkAclServiceImpl, networkAclManagerMock, networkAclItemDaoMock);
inOrder.verify(networkAclServiceImpl).createAclListIfNeeded(createNetworkAclCmdMock);
@ -392,13 +404,13 @@ public class NetworkACLServiceImplTest {
@Test(expected = InvalidParameterValueException.class)
public void validateNetworkAclTestAclNotDefaulWithoutVpc() {
Mockito.when(networkAclMock.getId()).thenReturn(3L);
Mockito.doReturn(null).when(entityManagerMock).findById(Vpc.class, networkMockVpcMockId);
Mockito.doReturn(null).when(vpcDaoMock).findById(networkMockVpcMockId);
networkAclServiceImpl.validateNetworkAcl(networkAclMock);
}
@Test
public void validateNetworkAclTestAclNotDefaulWithVpc() {
public void validateNetworkAclTestAclNotDefaultWithVpc() {
CallContext callContextMock = Mockito.mock(CallContext.class);
Mockito.doReturn(Mockito.mock(Account.class)).when(callContextMock).getCallingAccount();
@ -407,12 +419,11 @@ public class NetworkACLServiceImplTest {
Mockito.when(networkAclMock.getId()).thenReturn(3L);
Mockito.when(networkAclMock.getVpcId()).thenReturn(networkMockVpcMockId);
Mockito.doReturn(Mockito.mock(Vpc.class)).when(entityManagerMock).findById(Vpc.class, networkMockVpcMockId);
Mockito.doReturn(vpcVOMock).when(vpcDaoMock).findById(networkMockVpcMockId);
Mockito.doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
networkAclServiceImpl.validateNetworkAcl(networkAclMock);
Mockito.verify(entityManagerMock).findById(Vpc.class, networkMockVpcMockId);
Mockito.verify(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
callContextMocked.verify(() -> CallContext.current());
@ -702,16 +713,22 @@ public class NetworkACLServiceImplTest {
Mockito.doReturn(networkAclItemVoMock).when(networkAclServiceImpl).validateNetworkAclRuleIdAndRetrieveIt(updateNetworkACLItemCmdMock);
Mockito.doReturn(networkAclMock).when(networkAclManagerMock).getNetworkACL(networkAclMockId);
Mockito.doNothing().when(networkAclServiceImpl).validateNetworkAcl(Mockito.eq(networkAclMock));
Mockito.doNothing().when(networkAclServiceImpl).validateGlobalAclPermissionAndAclAssociatedToVpc(Mockito.any(NetworkACL.class), Mockito.any(Account.class), Mockito.anyString());
Mockito.doNothing().when(networkAclServiceImpl).transferDataToNetworkAclRulePojo(Mockito.eq(updateNetworkACLItemCmdMock), Mockito.eq(networkAclItemVoMock), Mockito.eq(networkAclMock));
Mockito.doNothing().when(networkAclServiceImpl).validateNetworkACLItem(networkAclItemVoMock);
Mockito.doReturn(networkAclItemVoMock).when(networkAclManagerMock).updateNetworkACLItem(networkAclItemVoMock);
CallContext callContextMock = Mockito.mock(CallContext.class);
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
Mockito.when(CallContext.current()).thenReturn(callContextMock);
networkAclServiceImpl.updateNetworkACLItem(updateNetworkACLItemCmdMock);
InOrder inOrder = Mockito.inOrder(networkAclServiceImpl, networkAclManagerMock);
inOrder.verify(networkAclServiceImpl).validateNetworkAclRuleIdAndRetrieveIt(updateNetworkACLItemCmdMock);
inOrder.verify(networkAclManagerMock).getNetworkACL(networkAclMockId);
inOrder.verify(networkAclServiceImpl).validateNetworkAcl(networkAclMock);
inOrder.verify(networkAclServiceImpl).validateGlobalAclPermissionAndAclAssociatedToVpc(networkAclMock, accountMock, "Only Root Admins can update global ACLs.");
inOrder.verify(networkAclServiceImpl).transferDataToNetworkAclRulePojo(Mockito.eq(updateNetworkACLItemCmdMock), Mockito.eq(networkAclItemVoMock), Mockito.eq(networkAclMock));
inOrder.verify(networkAclServiceImpl).validateNetworkACLItem(networkAclItemVoMock);
inOrder.verify(networkAclManagerMock).updateNetworkACLItem(networkAclItemVoMock);
@ -865,13 +882,18 @@ public class NetworkACLServiceImplTest {
Mockito.when(updateNetworkACLListCmdMock.getCustomId()).thenReturn(customId);
Mockito.when(updateNetworkACLListCmdMock.getId()).thenReturn(networkAclListId);
Mockito.when(updateNetworkACLListCmdMock.getDisplay()).thenReturn(false);
Mockito.when(networkACLVOMock.getVpcId()).thenReturn(networkMockVpcMockId);
Mockito.doReturn(vpcVOMock).when(vpcDaoMock).findById(networkMockVpcMockId);
Mockito.doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class),
Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
networkAclServiceImpl.updateNetworkACL(updateNetworkACLListCmdMock);
InOrder inOrder = Mockito.inOrder(networkAclDaoMock, entityManagerMock, entityManagerMock, accountManagerMock, networkACLVOMock);
InOrder inOrder = Mockito.inOrder(networkAclDaoMock, vpcDaoMock, accountManagerMock, networkACLVOMock);
inOrder.verify(networkAclDaoMock).findById(networkAclListId);
inOrder.verify(entityManagerMock).findById(Mockito.eq(Vpc.class), Mockito.anyLong());
inOrder.verify(vpcDaoMock).findById(Mockito.anyLong());
inOrder.verify(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.isNull(AccessType.class), Mockito.eq(true), nullable(Vpc.class));
@ -1063,13 +1085,17 @@ public class NetworkACLServiceImplTest {
Mockito.when(updateNetworkACLListCmdMock.getCustomId()).thenReturn(null);
Mockito.when(updateNetworkACLListCmdMock.getId()).thenReturn(networkAclListId);
Mockito.when(updateNetworkACLListCmdMock.getDisplay()).thenReturn(null);
Mockito.when(networkACLVOMock.getVpcId()).thenReturn(networkMockVpcMockId);
Mockito.doReturn(vpcVOMock).when(vpcDaoMock).findById(networkMockVpcMockId);
Mockito.doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class),
Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
networkAclServiceImpl.updateNetworkACL(updateNetworkACLListCmdMock);
InOrder inOrder = Mockito.inOrder(networkAclDaoMock, entityManagerMock, accountManagerMock, networkACLVOMock);
InOrder inOrder = Mockito.inOrder(networkAclDaoMock, vpcDaoMock, accountManagerMock, networkACLVOMock);
inOrder.verify(networkAclDaoMock).findById(networkAclListId);
inOrder.verify(entityManagerMock).findById(eq(Vpc.class), Mockito.anyLong());
inOrder.verify(vpcDaoMock).findById(Mockito.anyLong());
inOrder.verify(accountManagerMock).checkAccess(any(Account.class), isNull(), eq(true), nullable(Vpc.class));
Mockito.verify(networkACLVOMock, Mockito.times(0)).setName(null);
@ -1086,21 +1112,18 @@ public class NetworkACLServiceImplTest {
Mockito.when(nextAclRuleMock.getAclId()).thenReturn(networkAclMockId);
Mockito.when(previousAclRuleMock.getAclId()).thenReturn(networkAclMockId);
Mockito.doReturn(networkAclMock).when(networkAclDaoMock).findById(networkAclMockId);
Mockito.doReturn(Mockito.mock(Vpc.class)).when(entityManagerMock).findById(Vpc.class, networkMockVpcMockId);
CallContext callContextMock = Mockito.mock(CallContext.class);
Mockito.doReturn(Mockito.mock(Account.class)).when(callContextMock).getCallingAccount();
Mockito.doReturn(networkAclMock).when(networkAclDaoMock).findById(networkAclMockId);
Mockito.when(CallContext.current()).thenReturn(callContextMock);
Mockito.doNothing().when(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
Mockito.doNothing().when(networkAclServiceImpl).validateGlobalAclPermissionAndAclAssociatedToVpc(Mockito.any(NetworkACL.class), Mockito.any(Account.class), Mockito.anyString());
networkAclServiceImpl.validateMoveAclRulesData(aclRuleBeingMovedMock, previousAclRuleMock, nextAclRuleMock);
Mockito.verify(networkAclDaoMock).findById(networkAclMockId);
Mockito.verify(entityManagerMock).findById(Vpc.class, networkMockVpcMockId);
Mockito.verify(accountManagerMock).checkAccess(Mockito.any(Account.class), Mockito.isNull(AccessType.class), Mockito.eq(true), Mockito.any(Vpc.class));
Mockito.verify(networkAclServiceImpl).validateGlobalAclPermissionAndAclAssociatedToVpc(Mockito.any(NetworkACL.class), Mockito.any(Account.class), Mockito.anyString());
}
@Test
@ -1359,9 +1382,41 @@ public class NetworkACLServiceImplTest {
ArrayList<NetworkACLItemVO> allAclRules = new ArrayList<>();
allAclRules.add(networkAclItemVoMock);
Mockito.doReturn("someUuid").when(networkAclItemVoMock).getUuid();
Mockito.doReturn(SOME_UUID).when(networkAclItemVoMock).getUuid();
networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, allAclRules);
Mockito.verify(moveNetworkAclItemCmdMock, Mockito.times(1)).getAclConsistencyHash();
}
@Test
public void checkGlobalAclPermissionTestGlobalAclWithRootAccountShouldWork() {
Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
Mockito.doReturn(true).when(networkAclServiceImpl).isGlobalAcl(Mockito.anyLong());
networkAclServiceImpl.checkGlobalAclPermission(networkMockVpcMockId, accountMock, "exception");
}
@Test(expected = PermissionDeniedException.class)
public void checkGlobalAclPermissionTestGlobalAclWithNonRootAccountShouldThrow() {
Mockito.doReturn(Account.Type.NORMAL).when(accountMock).getType();
Mockito.doReturn(true).when(networkAclServiceImpl).isGlobalAcl(Mockito.anyLong());
networkAclServiceImpl.checkGlobalAclPermission(networkMockVpcMockId, accountMock, "exception");
}
@Test
public void validateAclAssociatedToVpcTestNonNullVpcShouldCheckAccess() {
Mockito.doReturn(vpcVOMock).when(vpcDaoMock).findById(Mockito.anyLong());
networkAclServiceImpl.validateAclAssociatedToVpc(networkMockVpcMockId, accountMock, SOME_UUID);
Mockito.verify(accountManagerMock, Mockito.times(1)).checkAccess(Mockito.any(Account.class), isNull(), Mockito.anyBoolean(), Mockito.any(Vpc.class));
}
@Test(expected = InvalidParameterValueException.class)
public void validateAclAssociatedToVpcTestNullVpcShouldThrowInvalidParameterValueException() {
Mockito.doReturn(null).when(vpcDaoMock).findById(Mockito.anyLong());
networkAclServiceImpl.validateAclAssociatedToVpc(networkMockVpcMockId, accountMock, SOME_UUID);
}
}

View File

@ -0,0 +1,245 @@
# 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.
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.utils import cleanup_resources
from marvin.lib.base import (Network, NetworkACLList, NetworkOffering, VpcOffering, VPC, NetworkACL)
from marvin.lib.common import (get_domain, get_zone)
from nose.plugins.attrib import attr
from marvin.cloudstackException import CloudstackAPIException
class Services:
"""Test Global ACLs
"""
def __init__(self):
self.services = {
"root_domain": {
"name": "ROOT",
},
"domain": {
"name": "Domain",
},
"user": {
"username": "user",
"roletype": 0,
},
"domain_admin": {
"username": "Domain admin",
"roletype": 2,
},
"root_admin": {
"username": "Root admin",
"roletype": 1,
},
"vpc": {
"name": "vpc-networkacl",
"displaytext": "vpc-networkacl",
"cidr": "10.1.1.0/24",
},
"vpcnetwork": {
"name": "vpcnetwork",
"displaytext": "vpcnetwork",
},
"rule": {
"protocol": "all",
"traffictype": "ingress",
}
}
class TestGlobalACLs(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestGlobalACLs, cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.services = Services().services
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
return
def setUp(self):
self.user_apiclient = self.testClient.getUserApiClient(self.services["user"]["username"],
self.services["domain"]["name"],
self.services["user"]["roletype"])
self.domain_admin_apiclient = self.testClient.getUserApiClient(self.services["domain_admin"]["username"],
self.services["domain"]["name"],
self.services["domain_admin"]["roletype"])
self.admin_apiclient = self.testClient.getUserApiClient(self.services["root_admin"]["username"],
self.services["root_domain"]["name"],
self.services["root_admin"]["roletype"])
self.cleanup = []
return
def tearDown(self):
super(TestGlobalACLs, self).tearDown()
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_create_global_acl(self):
""" Test create global ACL as a normal user, domain admin and root admin users.
"""
self.debug("Creating ACL list as a normal user, should raise exception.")
self.assertRaisesRegex(CloudstackAPIException, "Only Root Admin can create global ACLs.",
NetworkACLList.create, apiclient=self.user_apiclient, services={},
name="acl", description="acl")
self.debug("Creating ACL list as a domain admin, should raise exception.")
self.assertRaisesRegex(CloudstackAPIException, "Only Root Admin can create global ACLs.",
NetworkACLList.create, apiclient=self.domain_admin_apiclient, services={},
name="acl", description="acl")
self.debug("Creating ACL list as a root admin, should work.")
acl = NetworkACLList.create(apiclient=self.admin_apiclient, services={}, name="acl", description="acl")
self.cleanup.append(acl)
self.assertIsNotNone(acl, "A root admin user should be able to create a global ACL.")
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_replace_acl_of_network(self):
""" Test to replace ACL of a VPC as a normal user, domain admin and root admin users.
"""
# Get network offering
networkOffering = NetworkOffering.list(self.apiclient, name="DefaultIsolatedNetworkOfferingForVpcNetworks")
self.assertTrue(networkOffering is not None and len(networkOffering) > 0, "No VPC network offering")
# Getting VPC offering
vpcOffering = VpcOffering.list(self.apiclient, name="Default VPC offering")
self.assertTrue(vpcOffering is not None and len(vpcOffering) > 0, "No VPC offerings found")
# Creating VPC
vpc = VPC.create(
apiclient=self.apiclient,
services=self.services["vpc"],
networkDomain="vpc.networkacl",
vpcofferingid=vpcOffering[0].id,
zoneid=self.zone.id,
domainid=self.domain.id
)
self.cleanup.append(vpc)
self.assertTrue(vpc is not None, "VPC creation failed")
# Creating ACL list
acl = NetworkACLList.create(apiclient=self.apiclient, services={}, name="acl", description="acl")
# Creating tier on VPC with ACL list
network = Network.create(
apiclient=self.apiclient,
services=self.services["vpcnetwork"],
accountid="Admin",
domainid=self.domain.id,
networkofferingid=networkOffering[0].id,
zoneid=self.zone.id,
vpcid=vpc.id,
aclid=acl.id,
gateway="10.1.1.1",
netmask="255.255.255.192"
)
self.cleanup.append(network)
# User should be able to replace ACL
network.replaceACLList(apiclient=self.user_apiclient, aclid=acl.id)
# Domain Admin should be able to replace ACL
network.replaceACLList(apiclient=self.domain_admin_apiclient, aclid=acl.id)
# Admin should be able to replace ACL
network.replaceACLList(apiclient=self.admin_apiclient, aclid=acl.id)
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_create_acl_rule(self):
""" Test to create ACL rule as a normal user, domain admin and root admin users.
"""
# Creating ACL list
acl = NetworkACLList.create(apiclient=self.admin_apiclient, services={}, name="acl", description="acl")
self.cleanup.append(acl)
self.debug("Creating ACL rule as a user, should raise exception.")
self.assertRaisesRegex(CloudstackAPIException, "Only Root Admins can create rules for a global ACL.",
NetworkACL.create, self.user_apiclient, services=self.services["rule"], aclid=acl.id)
self.debug("Creating ACL rule as a domain admin, should raise exception.")
self.assertRaisesRegex(CloudstackAPIException, "Only Root Admins can create rules for a global ACL.",
NetworkACL.create, self.domain_admin_apiclient, services=self.services["rule"], aclid=acl.id)
self.debug("Creating ACL rule as a root admin, should work.")
acl_rule = NetworkACL.create(self.admin_apiclient, services=self.services["rule"], aclid=acl.id)
self.cleanup.append(acl_rule)
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_delete_acl_rule(self):
""" Test to delete ACL rule as a normal user, domain admin and root admin users.
"""
# Creating ACL list
acl = NetworkACLList.create(apiclient=self.apiclient, services={}, name="acl", description="acl")
self.cleanup.append(acl)
# Creating ACL rule
acl_rule = NetworkACL.create(self.apiclient, services=self.services["rule"], aclid=acl.id)
self.cleanup.append(acl_rule)
self.debug("Deleting ACL rule as a user, should raise exception.")
self.assertRaisesRegex(Exception, "Only Root Admin can delete global ACL rules.",
NetworkACL.delete, acl_rule, self.user_apiclient)
self.debug("Deleting ACL rule as a domain admin, should raise exception.")
self.assertRaisesRegex(Exception, "Only Root Admin can delete global ACL rules.",
NetworkACL.delete, acl_rule, self.domain_admin_apiclient)
self.debug("Deleting ACL rule as a root admin, should work.")
NetworkACL.delete(acl_rule, self.admin_apiclient)
self.cleanup.remove(acl_rule)
# Verify if the number of ACL rules is equal to four, i.e. the number of rules
# for the default ACLs `default_allow` (2 rules) and `default_deny` (2 rules) ACLs
number_of_acl_rules = acl_rule.list(apiclient=self.admin_apiclient)
self.assertEqual(len(number_of_acl_rules), 4)
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_delete_global_acl(self):
""" Test delete global ACL as a normal user, domain admin and root admin users.
"""
# Creating ACL list. Not adding to cleanup as it will be deleted in this method
acl = NetworkACLList.create(apiclient=self.apiclient, services={}, name="acl", description="acl")
self.cleanup.append(acl)
self.debug("Deleting ACL list as a normal user, should raise exception.")
self.assertRaisesRegex(Exception, "Only Root Admin can delete global ACLs.",
NetworkACLList.delete, acl, apiclient=self.user_apiclient)
self.debug("Deleting ACL list as a domain admin, should raise exception.")
self.assertRaisesRegex(Exception, "Only Root Admin can delete global ACLs.",
NetworkACLList.delete, acl, apiclient=self.domain_admin_apiclient)
self.debug("Deleting ACL list as a root admin, should work.")
acl.delete(apiclient=self.admin_apiclient)
self.cleanup.remove(acl)
# Verify if number of ACLs is equal to two, i.e. the number of default ACLs `default_allow` and `default_deny`
number_of_acls = NetworkACLList.list(apiclient=self.admin_apiclient)
self.assertEqual(len(number_of_acls), 2)
return