2424
2525import javax .inject .Inject ;
2626
27+ import com .cloud .exception .PermissionDeniedException ;
2728import org .apache .cloudstack .api .ApiErrorCode ;
2829import org .apache .cloudstack .api .ServerApiException ;
2930import org .apache .cloudstack .api .command .user .network .CreateNetworkACLCmd ;
@@ -103,12 +104,15 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
103104
104105 @ Override
105106 public NetworkACL createNetworkACL (final String name , final String description , final long vpcId , final Boolean forDisplay ) {
106- final Account caller = CallContext .current ().getCallingAccount ();
107- final Vpc vpc = _entityMgr .findById (Vpc .class , vpcId );
108- if (vpc == null ) {
109- throw new InvalidParameterValueException ("Unable to find VPC" );
107+ if (vpcId != 0 ) {
108+ final Account caller = CallContext .current ().getCallingAccount ();
109+ final Vpc vpc = _vpcDao .findById (vpcId );
110+ if (vpc == null ) {
111+ throw new InvalidParameterValueException (String .format ("Unable to find VPC with ID [%s]." , vpcId ));
112+ }
113+ _accountMgr .checkAccess (caller , null , true , vpc );
114+
110115 }
111- _accountMgr .checkAccess (caller , null , true , vpc );
112116 return _networkAclMgr .createNetworkACL (name , description , vpcId , forDisplay );
113117 }
114118
@@ -212,22 +216,17 @@ public Pair<List<? extends NetworkACL>, Integer> listNetworkACLs(final ListNetwo
212216 @ Override
213217 @ ActionEvent (eventType = EventTypes .EVENT_NETWORK_ACL_DELETE , eventDescription = "Deleting Network ACL List" , async = true )
214218 public boolean deleteNetworkACL (final long id ) {
215- final Account caller = CallContext .current ().getCallingAccount ();
216219 final NetworkACL acl = _networkACLDao .findById (id );
220+ Account account = CallContext .current ().getCallingAccount ();
217221 if (acl == null ) {
218222 throw new InvalidParameterValueException ("Unable to find specified ACL" );
219223 }
220224
221- //Do not allow deletion of default ACLs
222- if (acl .getId () == NetworkACL .DEFAULT_ALLOW || acl .getId () == NetworkACL .DEFAULT_DENY ) {
225+ if (isDefaultAcl (acl .getId ())) {
223226 throw new InvalidParameterValueException ("Default ACL cannot be removed" );
224227 }
225228
226- final Vpc vpc = _entityMgr .findById (Vpc .class , acl .getVpcId ());
227- if (vpc == null ) {
228- throw new InvalidParameterValueException ("Unable to find specified VPC associated with the ACL" );
229- }
230- _accountMgr .checkAccess (caller , null , true , vpc );
229+ validateGlobalAclPermissionAndAclAssociatedToVpc (acl , account , "Only Root Admin can delete global ACLs." );
231230 return _networkAclMgr .deleteNetworkACL (acl );
232231 }
233232
@@ -253,7 +252,7 @@ public boolean replaceNetworkACLonPrivateGw(final long aclId, final long private
253252 throw new InvalidParameterValueException ("Unable to find specified vpc id" );
254253 }
255254
256- if (aclId != NetworkACL . DEFAULT_DENY && aclId != NetworkACL . DEFAULT_ALLOW ) {
255+ if (! isDefaultAcl ( aclId ) ) {
257256 final Vpc vpc = _entityMgr .findById (Vpc .class , acl .getVpcId ());
258257 if (vpc == null ) {
259258 throw new InvalidParameterValueException ("Unable to find Vpc associated with the NetworkACL" );
@@ -293,15 +292,9 @@ public boolean replaceNetworkACL(final long aclId, final long networkId) throws
293292 throw new InvalidParameterValueException ("Network ACL can be created just for networks of type " + Networks .TrafficType .Guest );
294293 }
295294
296- if (aclId != NetworkACL .DEFAULT_DENY && aclId != NetworkACL .DEFAULT_ALLOW ) {
297- //ACL is not default DENY/ALLOW
298- // ACL should be associated with a VPC
299- final Vpc vpc = _entityMgr .findById (Vpc .class , acl .getVpcId ());
300- if (vpc == null ) {
301- throw new InvalidParameterValueException ("Unable to find Vpc associated with the NetworkACL" );
302- }
295+ if (!isDefaultAcl (aclId ) && !isGlobalAcl (acl .getVpcId ())) {
296+ validateAclAssociatedToVpc (acl .getVpcId (), caller , acl .getUuid ());
303297
304- _accountMgr .checkAccess (caller , null , true , vpc );
305298 if (!network .getVpcId ().equals (acl .getVpcId ())) {
306299 throw new InvalidParameterValueException ("Network: " + networkId + " and ACL: " + aclId + " do not belong to the same VPC" );
307300 }
@@ -340,6 +333,11 @@ public NetworkACLItem createNetworkACLItem(CreateNetworkACLCmd createNetworkACLC
340333 NetworkACL acl = _networkAclMgr .getNetworkACL (aclId );
341334
342335 validateNetworkAcl (acl );
336+ Account caller = CallContext .current ().getCallingAccount ();
337+
338+ if (isGlobalAcl (acl .getVpcId ()) && !Account .Type .ADMIN .equals (caller .getType ())) {
339+ throw new PermissionDeniedException ("Only Root Admins can create rules for a global ACL." );
340+ }
343341 validateAclRuleNumber (createNetworkACLCmd , acl );
344342
345343 NetworkACLItem .Action ruleAction = validateAndCreateNetworkAclRuleAction (action );
@@ -409,7 +407,8 @@ protected void validateAclRuleNumber(CreateNetworkACLCmd createNetworkAclCmd, Ne
409407 * <ul>
410408 * <li>If the parameter is null, we return an {@link InvalidParameterValueException};
411409 * <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};
412- * <li>If the network does not have a VPC, we will throw an {@link InvalidParameterValueException}.
410+ * <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
411+ * {@link InvalidParameterValueException}.
413412 * </ul>
414413 *
415414 * 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 @@ protected void validateNetworkAcl(NetworkACL acl) {
419418 throw new InvalidParameterValueException ("Unable to find specified ACL." );
420419 }
421420
422- if (acl . getId () == NetworkACL . DEFAULT_DENY || acl .getId () == NetworkACL . DEFAULT_ALLOW ) {
421+ if (isDefaultAcl ( acl .getId ()) ) {
423422 throw new InvalidParameterValueException ("Default ACL cannot be modified" );
424423 }
425424
426- Vpc vpc = _entityMgr . findById ( Vpc . class , acl .getVpcId () );
427- if (vpc == null ) {
428- throw new InvalidParameterValueException ( String . format ( "Unable to find Vpc associated with the NetworkACL [%s]" , acl .getUuid () ));
425+ Long aclVpcId = acl .getVpcId ();
426+ if (! isGlobalAcl ( aclVpcId ) ) {
427+ validateAclAssociatedToVpc ( aclVpcId , CallContext . current (). getCallingAccount () , acl .getUuid ());
429428 }
430- Account caller = CallContext .current ().getCallingAccount ();
431- _accountMgr .checkAccess (caller , null , true , vpc );
432429 }
433430
434431 /**
@@ -792,17 +789,12 @@ public boolean revokeNetworkACLItem(final long ruleId) {
792789 final NetworkACLItemVO aclItem = _networkACLItemDao .findById (ruleId );
793790 if (aclItem != null ) {
794791 final NetworkACL acl = _networkAclMgr .getNetworkACL (aclItem .getAclId ());
792+ final Account account = CallContext .current ().getCallingAccount ();
795793
796- final Vpc vpc = _entityMgr .findById (Vpc .class , acl .getVpcId ());
797-
798- if (aclItem .getAclId () == NetworkACL .DEFAULT_ALLOW || aclItem .getAclId () == NetworkACL .DEFAULT_DENY ) {
794+ if (isDefaultAcl (aclItem .getAclId ())) {
799795 throw new InvalidParameterValueException ("ACL Items in default ACL cannot be deleted" );
800796 }
801-
802- final Account caller = CallContext .current ().getCallingAccount ();
803-
804- _accountMgr .checkAccess (caller , null , true , vpc );
805-
797+ validateGlobalAclPermissionAndAclAssociatedToVpc (acl , account , "Only Root Admin can delete global ACL rules." );
806798 }
807799 return _networkAclMgr .revokeNetworkACLItem (ruleId );
808800 }
@@ -826,6 +818,9 @@ public NetworkACLItem updateNetworkACLItem(UpdateNetworkACLItemCmd updateNetwork
826818 NetworkACL acl = _networkAclMgr .getNetworkACL (networkACLItemVo .getAclId ());
827819 validateNetworkAcl (acl );
828820
821+ Account account = CallContext .current ().getCallingAccount ();
822+ validateGlobalAclPermissionAndAclAssociatedToVpc (acl , account , "Only Root Admins can update global ACLs." );
823+
829824 transferDataToNetworkAclRulePojo (updateNetworkACLItemCmd , networkACLItemVo , acl );
830825 validateNetworkACLItem (networkACLItemVo );
831826 return _networkAclMgr .updateNetworkACLItem (networkACLItemVo );
@@ -912,14 +907,13 @@ protected NetworkACLItemVO validateNetworkAclRuleIdAndRetrieveIt(UpdateNetworkAC
912907 }
913908
914909 @ Override
915- @ ActionEvent (eventType = EventTypes .EVENT_NETWORK_ACL_UPDATE , eventDescription = "updating network acl " , async = true )
910+ @ ActionEvent (eventType = EventTypes .EVENT_NETWORK_ACL_UPDATE , eventDescription = "Updating Network ACL " , async = true )
916911 public NetworkACL updateNetworkACL (UpdateNetworkACLListCmd updateNetworkACLListCmd ) {
917912 Long id = updateNetworkACLListCmd .getId ();
918913 NetworkACLVO acl = _networkACLDao .findById (id );
919- Vpc vpc = _entityMgr . findById ( Vpc . class , acl . getVpcId () );
914+ Account account = CallContext . current (). getCallingAccount ( );
920915
921- Account caller = CallContext .current ().getCallingAccount ();
922- _accountMgr .checkAccess (caller , null , true , vpc );
916+ validateGlobalAclPermissionAndAclAssociatedToVpc (acl , account , "Must be Root Admin to update a global ACL." );
923917
924918 String name = updateNetworkACLListCmd .getName ();
925919 if (StringUtils .isNotBlank (name )) {
@@ -1149,14 +1143,59 @@ protected void validateMoveAclRulesData(NetworkACLItemVO ruleBeingMoved, Network
11491143 long aclId = ruleBeingMoved .getAclId ();
11501144
11511145 if ((nextRule != null && nextRule .getAclId () != aclId ) || (previousRule != null && previousRule .getAclId () != aclId )) {
1152- throw new InvalidParameterValueException ("Cannot use ACL rules from differenting ACLs. Rule being moved." );
1146+ throw new InvalidParameterValueException ("Cannot use ACL rules from differentiating ACLs. Rule being moved." );
11531147 }
11541148 NetworkACLVO acl = _networkACLDao .findById (aclId );
1155- Vpc vpc = _entityMgr .findById (Vpc .class , acl .getVpcId ());
1149+ Account account = CallContext .current ().getCallingAccount ();
1150+
1151+ if (isDefaultAcl (aclId )) {
1152+ throw new InvalidParameterValueException ("Default ACL rules cannot be moved." );
1153+ }
1154+
1155+ validateGlobalAclPermissionAndAclAssociatedToVpc (acl , account ,"Must be Root Admin to move global ACL rules." );
1156+ }
1157+
1158+ /**
1159+ * 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
1160+ * does not have permission.
1161+ */
1162+ protected void checkGlobalAclPermission (Long aclVpcId , Account account , String exceptionMessage ) {
1163+ if (isGlobalAcl (aclVpcId ) && !Account .Type .ADMIN .equals (account .getType ())) {
1164+ throw new PermissionDeniedException (exceptionMessage );
1165+ }
1166+ }
1167+
1168+ protected void validateAclAssociatedToVpc (Long aclVpcId , Account account , String aclUuid ) {
1169+ final Vpc vpc = _vpcDao .findById (aclVpcId );
11561170 if (vpc == null ) {
1157- throw new InvalidParameterValueException ("Re-ordering rules for a default ACL is prohibited" );
1171+ throw new InvalidParameterValueException (String . format ( "Unable to find specified VPC [%s] associated with the ACL [%s]." , aclVpcId , aclUuid ) );
11581172 }
1159- Account caller = CallContext .current ().getCallingAccount ();
1160- _accountMgr .checkAccess (caller , null , true , vpc );
1173+ _accountMgr .checkAccess (account , null , true , vpc );
1174+ }
1175+
1176+ /**
1177+ * It performs two different verifications depending on if the ACL is global or not.
1178+ * <ul>
1179+ * <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.
1180+ * <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
1181+ * aclVpcId} is from a valid VPC.
1182+ * </ul>
1183+ */
1184+ protected void validateGlobalAclPermissionAndAclAssociatedToVpc (NetworkACL acl , Account account , String exception ){
1185+ if (isGlobalAcl (acl .getVpcId ())) {
1186+ s_logger .info (String .format ("Checking if account [%s] has permission to manipulate global ACL [%s]." , account , acl ));
1187+ checkGlobalAclPermission (acl .getVpcId (), account , exception );
1188+ } else {
1189+ s_logger .info (String .format ("Validating ACL [%s] associated to VPC [%s] with account [%s]." , acl , acl .getVpcId (), account ));
1190+ validateAclAssociatedToVpc (acl .getVpcId (), account , acl .getUuid ());
1191+ }
1192+ }
1193+
1194+ protected boolean isGlobalAcl (Long aclVpcId ) {
1195+ return aclVpcId != null && aclVpcId == 0 ;
1196+ }
1197+
1198+ protected boolean isDefaultAcl (long aclId ) {
1199+ return aclId == NetworkACL .DEFAULT_ALLOW || aclId == NetworkACL .DEFAULT_DENY ;
11611200 }
11621201}
0 commit comments