From cb62ce67671699fa01564b3b4b0d3d83eb3d5acb Mon Sep 17 00:00:00 2001
From: Bryan Lima <42067040+BryanMLima@users.noreply.github.com>
Date: Thu, 30 Nov 2023 10:51:43 -0300
Subject: [PATCH] Global ACL for VPCs (#7150)
---
.github/workflows/ci.yml | 3 +-
.../user/network/CreateNetworkACLListCmd.java | 36 ++-
.../com/cloud/network/vpc/NetworkACLVO.java | 7 +
.../com/cloud/network/NetworkServiceImpl.java | 17 +-
.../network/vpc/NetworkACLServiceImpl.java | 133 ++++++----
.../com/cloud/network/vpc/VpcManagerImpl.java | 14 +-
.../vpc/NetworkACLServiceImplTest.java | 153 +++++++----
test/integration/smoke/test_global_acls.py | 245 ++++++++++++++++++
8 files changed, 494 insertions(+), 114 deletions(-)
create mode 100644 test/integration/smoke/test_global_acls.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d6b5f5a1c4f..77c9d59505c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java
index 14dbfcafd7b..e5dbcc7b6d1 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLListCmd.java
@@ -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;
+ }
}
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java
index 4eaa2b575e0..280d5dfaf4b 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java
@@ -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;
}
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index f9f80cb5b76..18f98e6f99f 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -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();
diff --git a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
index ed37eb5b375..8139ac1c49e 100644
--- a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
@@ -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
*
* - If the parameter is null, we return an {@link InvalidParameterValueException};
*
- 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};
- *
- If the network does not have a VPC, we will throw an {@link InvalidParameterValueException}.
+ *
- 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}.
*
*
* 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.
+ *
+ * - 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.
+ *
- 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.
+ *
+ */
+ 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;
}
}
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index 6bc70a9cef4..1f99d164625 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1977,16 +1977,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
searchBuilder.and("vpcId", searchBuilder.entity().getVpcId(), Op.IN);
final SearchCriteria 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, Integer> aclsCountPair = _networkAclDao.searchAndCount(searchCriteria, filter);
final List 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;
+ }
}
diff --git a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
index 8dd3f32d6c4..18a072172ad 100644
--- a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
+++ b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
@@ -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 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 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);
+ }
}
diff --git a/test/integration/smoke/test_global_acls.py b/test/integration/smoke/test_global_acls.py
new file mode 100644
index 00000000000..47db858c121
--- /dev/null
+++ b/test/integration/smoke/test_global_acls.py
@@ -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