You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pr...@apache.org on 2012/11/30 01:10:41 UTC

[2/2] git commit: Some ACL POC work

Some ACL POC work

Conflicts:

	server/src/com/cloud/api/ApiDispatcher.java


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/07386324
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/07386324
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/07386324

Branch: refs/heads/api_refactoring
Commit: 073863249abf36b6879359889f5731984391fa41
Parents: a526460
Author: Prachi Damle <pr...@cloud.com>
Authored: Thu Nov 29 16:09:47 2012 -0800
Committer: Prachi Damle <pr...@cloud.com>
Committed: Thu Nov 29 16:09:47 2012 -0800

----------------------------------------------------------------------
 api/src/com/cloud/api/ACL.java                  |   31 +++++
 api/src/com/cloud/api/commands/DeployVMCmd.java |    9 ++
 server/src/com/cloud/api/ApiDispatcher.java     |  127 +++++++++++++++++-
 server/src/com/cloud/api/ApiServer.java         |   11 ++-
 4 files changed, 173 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/api/src/com/cloud/api/ACL.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/ACL.java b/api/src/com/cloud/api/ACL.java
new file mode 100644
index 0000000..1f376e9
--- /dev/null
+++ b/api/src/com/cloud/api/ACL.java
@@ -0,0 +1,31 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.api;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ FIELD })
+public @interface ACL {
+	
+
+	Class<?> resourceType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/api/src/com/cloud/api/commands/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java
index f67ee8f..da9f3ea 100644
--- a/api/src/com/cloud/api/commands/DeployVMCmd.java
+++ b/api/src/com/cloud/api/commands/DeployVMCmd.java
@@ -26,6 +26,7 @@ import java.util.Map;
 
 import org.apache.log4j.Logger;
 
+import com.cloud.api.ACL;
 import com.cloud.api.ApiConstants;
 import com.cloud.api.BaseAsyncCreateCmd;
 import com.cloud.api.BaseCmd;
@@ -43,7 +44,10 @@ import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.Network;
+import com.cloud.network.security.SecurityGroup;
 import com.cloud.offering.DiskOffering;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.template.VirtualMachineTemplate;
@@ -69,6 +73,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine")
     private Long serviceOfferingId;
 
+    @ACL(resourceType=VirtualMachineTemplate.class)
     @IdentityMapper(entityTableName="vm_template")
     @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine")
     private Long templateId;
@@ -88,6 +93,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     private Long domainId;
 
     //Network information
+    @ACL(resourceType=Network.class)
     @IdentityMapper(entityTableName="networks")
     @Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter")
     private List<Long> networkIds;
@@ -112,14 +118,17 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     @Parameter(name=ApiConstants.SSH_KEYPAIR, type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine")
     private String sshKeyPairName;
 
+    //@ACL(resourceType=Host.class)
     @IdentityMapper(entityTableName="host")
     @Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, description="destination Host ID to deploy the VM to - parameter available for root admin only")
     private Long hostId;
     
+    //@ACL(resourceType=SecurityGroup.class)
     @IdentityMapper(entityTableName="security_group")
     @Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter")
     private List<Long> securityGroupIdList;
     
+    //@ACL(resourceType=SecurityGroup.class)
     @Parameter(name=ApiConstants.SECURITY_GROUP_NAMES, type=CommandType.LIST, collectionType=CommandType.STRING, description="comma separated list of security groups names that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter")
     private List<String> securityGroupNameList;
     

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/server/src/com/cloud/api/ApiDispatcher.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java
index dfe4a1f..471dfc4 100755
--- a/server/src/com/cloud/api/ApiDispatcher.java
+++ b/server/src/com/cloud/api/ApiDispatcher.java
@@ -22,6 +22,7 @@ import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -29,6 +30,7 @@ import java.util.regex.Matcher;
 
 import org.apache.log4j.Logger;
 
+import com.cloud.acl.ControlledEntity;
 import com.cloud.api.BaseCmd.CommandType;
 import com.cloud.api.commands.ListEventsCmd;
 import com.cloud.async.AsyncCommandQueued;
@@ -42,14 +44,19 @@ import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.utils.IdentityProxy;
+import com.cloud.network.dao.NetworkDao;
 import com.cloud.server.ManagementServer;
+import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountService;
 import com.cloud.user.UserContext;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.IdentityProxy;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.db.GenericDao;
 import com.cloud.utils.exception.CSExceptionErrorCode;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.uuididentity.dao.IdentityDao;
@@ -64,7 +71,10 @@ public class ApiDispatcher {
     AsyncJobManager _asyncMgr;
     IdentityDao _identityDao;
     Long _createSnapshotQueueSizeLimit;
+    AccountManager _accountMgr;
 
+
+    Map<String, Class<? extends GenericDao>> _daoNameMap = new HashMap<String, Class<? extends GenericDao>>();
     // singleton class
     private static ApiDispatcher s_instance = new ApiDispatcher();
 
@@ -76,6 +86,7 @@ public class ApiDispatcher {
         _locator = ComponentLocator.getLocator(ManagementServer.Name);
         _asyncMgr = _locator.getManager(AsyncJobManager.class);
         _identityDao = _locator.getDao(IdentityDao.class);
+
         ConfigurationDao configDao = _locator.getDao(ConfigurationDao.class);
         Map<String, String> configs = configDao.getConfiguration();
         String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key());
@@ -88,13 +99,30 @@ public class ApiDispatcher {
                 _createSnapshotQueueSizeLimit = snapshotLimit;
             }
         }
+        _accountMgr = _locator.getManager(AccountManager.class);
+        
+        _daoNameMap.put("com.cloud.network.Network", NetworkDao.class);
+        _daoNameMap.put("com.cloud.template.VirtualMachineTemplate", VMTemplateDao.class);
+        
+        
     }
 
     public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map<String, String> params) {
 
-        setupParameters(cmd, params);
+    	List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+    	setupParameters(cmd, params, entitiesToAccess);
         plugService(cmd);
 
+        if(!entitiesToAccess.isEmpty()){
+			 //owner
+			Account caller = UserContext.current().getCaller();
+			Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+			s_instance._accountMgr.checkAccess(caller, null, true, owner);
+			 
+			for(ControlledEntity entity : entitiesToAccess)
+			s_instance._accountMgr.checkAccess(caller, null, true, entity);
+        }
+        
         try {
             UserContext ctx = UserContext.current();
             ctx.setAccountId(cmd.getEntityOwnerId());
@@ -135,8 +163,19 @@ public class ApiDispatcher {
     }
 
     public void dispatch(BaseCmd cmd, Map<String, String> params) {
-        setupParameters(cmd, params);
+    	List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+    	setupParameters(cmd, params, entitiesToAccess);
         ApiDispatcher.plugService(cmd);
+        
+        if(!entitiesToAccess.isEmpty()){
+			 //owner
+			Account caller = UserContext.current().getCaller();
+			Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+			s_instance._accountMgr.checkAccess(caller, null, true, owner);
+			for(ControlledEntity entity : entitiesToAccess)
+			s_instance._accountMgr.checkAccess(caller, null, true, entity);
+        }
+        
         try {
             UserContext ctx = UserContext.current();
             ctx.setAccountId(cmd.getEntityOwnerId());
@@ -299,8 +338,10 @@ public class ApiDispatcher {
         }
     }
 
-    public static void setupParameters(BaseCmd cmd, Map<String, String> params) {
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+	public static void setupParameters(BaseCmd cmd, Map<String, String> params, List<ControlledEntity> entitiesToAccess) {
         Map<String, Object> unpackedParams = cmd.unpackParams(params);
+        
 
         if (cmd instanceof BaseListCmd) {
             Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE);
@@ -368,12 +409,90 @@ public class ApiDispatcher {
                 throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage());
             } catch (CloudRuntimeException cloudEx) {
                 // FIXME: Better error message? This only happens if the API command is not executable, which typically
-// means
+            	//means
                 // there was
                 // and IllegalAccessException setting one of the parameters.
                 throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
             }
+            
+            
+            //check access on the resource this field points to
+	        try {
+	            ACL checkAccess = field.getAnnotation(ACL.class);
+	            CommandType fieldType = parameterAnnotation.type();
+	            
+	            
+	            if(checkAccess != null){
+	            	// Verify that caller can perform actions in behalf of vm owner
+	            	//acumulate all Controlled Entities together.
+	            	if(checkAccess.resourceType() != null){
+	            		 Class<?> entity = checkAccess.resourceType();
+	            				 
+	            		 if(ControlledEntity.class.isAssignableFrom(entity)){
+	                         if (s_logger.isDebugEnabled()) {
+	                             s_logger.debug("entity name is:" + entity.getName());
+	                         }
+	                         
+	                         if(s_instance._daoNameMap.containsKey(entity.getName())){
+	                        	 Class<? extends GenericDao> daoClass = s_instance._daoNameMap.get(entity.getName());
+	                        	 GenericDao daoClassInstance =  s_instance._locator.getDao(daoClass);
+	                        	 
+	                        	 //Check if the parameter type is a single Id or list of id's/name's
+	                        	 switch (fieldType) {                        	 
+			                         case LIST:
+		                                 CommandType listType = parameterAnnotation.collectionType();
+		                                 switch (listType) {
+    		                                 case LONG: 
+    		                                	 List<Long> listParam = new ArrayList<Long>();
+     											 listParam = (List)field.get(cmd);
+    
+    		                                	 for(Long entityId : listParam){
+    			    	                        	 ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
+    			    	                        	 entitiesToAccess.add(entityObj);
+    		                                	 }
+    			    	                     break;
+    		                                 /*case STRING:
+    		                                	 List<String> listParam = new ArrayList<String>();
+    		                                	 listParam = (List)field.get(cmd);
+    		                                	 for(String entityName: listParam){
+    			    	                        	 ControlledEntity entityObj = (ControlledEntity)daoClassInstance(entityId);
+    			    	                        	 entitiesToAccess.add(entityObj);
+    		                                	 }
+    		                                     break;
+    		                                  */
+    		                                 default:
+    		                                 break;
+		                                 }
+			                             break;
+			                         case LONG:
+			                        	 Long entityId = (Long)field.get(cmd);
+			                        	 ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
+			                        	 entitiesToAccess.add(entityObj);
+			                        	 break;
+			                         default:
+			                        	 break;
+	                        	 }
+	                             
+	                        	 
+	                         }
+	            			 
+	            		 }
+	            		 
+	            	}
+	            	
+	            }
+	
+			} catch (IllegalArgumentException e) {
+	            s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
+	            throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
+			} catch (IllegalAccessException e) {
+	            s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
+	            throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
+			}
+            
         }
+        
+        //check access on the entities.
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index a5c9ea5..cfd3b25 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -81,6 +81,7 @@ import org.apache.http.protocol.ResponseDate;
 import org.apache.http.protocol.ResponseServer;
 import org.apache.log4j.Logger;
 
+import com.cloud.acl.ControlledEntity;
 import com.cloud.api.response.ApiResponseSerializer;
 import com.cloud.api.response.ExceptionResponse;
 import com.cloud.api.response.ListResponse;
@@ -487,8 +488,16 @@ public class ApiServer implements HttpRequestHandler {
                 objectEntityTable = createCmd.getEntityTable();
                 params.put("id", objectId.toString());
             } else {
-                ApiDispatcher.setupParameters(cmdObj, params);
+            	List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+                ApiDispatcher.setupParameters(cmdObj, params, entitiesToAccess);
                 ApiDispatcher.plugService(cmdObj);
+                
+                if(!entitiesToAccess.isEmpty()){
+	                Account owner = s_instance._accountMgr.getActiveAccountById(cmdObj.getEntityOwnerId());
+	        		s_instance._accountMgr.checkAccess(caller, null, true, owner);
+	        		 
+	        		s_instance._accountMgr.checkAccess(caller, null, true, (ControlledEntity[])entitiesToAccess.toArray());
+                }
             }
 
             BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj;