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/19 23:05:51 UTC

git commit: some more poc work

Updated Branches:
  refs/heads/acl 3058520ab -> dbbe96c90


some more poc work


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

Branch: refs/heads/acl
Commit: dbbe96c905412e728231a6d863cca0fd34384442
Parents: 3058520
Author: Prachi Damle <pr...@cloud.com>
Authored: Tue Nov 13 11:47:38 2012 -0800
Committer: Prachi Damle <pr...@cloud.com>
Committed: Tue Nov 13 11:47:38 2012 -0800

----------------------------------------------------------------------
 api/src/com/cloud/acl/APIAccessChecker.java        |   14 +
 api/src/com/cloud/acl/Role.java                    |   20 ++
 api/src/com/cloud/api/ACL.java                     |    4 +-
 api/src/com/cloud/api/Parameter.java               |    4 +
 api/src/com/cloud/api/commands/DeployVMCmd.java    |   31 ++-
 api/src/com/cloud/domain/Domain.java               |    1 +
 .../com/cloud/network/security/SecurityGroup.java  |    1 +
 api/src/com/cloud/user/Account.java                |    2 +
 .../cloud/acl/StaticRoleBasedAPIAccessChecker.java |  188 +++++++++++++++
 server/src/com/cloud/api/ApiDispatcher.java        |  128 ++++++----
 server/src/com/cloud/api/ApiServer.java            |  159 ++-----------
 server/src/com/cloud/host/dao/HostDaoImpl.java     |    2 +-
 12 files changed, 349 insertions(+), 205 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/acl/APIAccessChecker.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/acl/APIAccessChecker.java b/api/src/com/cloud/acl/APIAccessChecker.java
new file mode 100644
index 0000000..c0f8fd4
--- /dev/null
+++ b/api/src/com/cloud/acl/APIAccessChecker.java
@@ -0,0 +1,14 @@
+package com.cloud.acl;
+
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.user.Account;
+import com.cloud.user.User;
+import com.cloud.utils.component.Adapter;
+
+/**
+ * APIAccessChecker checks the ownership and access control to API requests 
+ */
+public interface APIAccessChecker extends Adapter {
+	
+	boolean canAccessAPI(User user, String apiCommandName) throws PermissionDeniedException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/acl/Role.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/acl/Role.java b/api/src/com/cloud/acl/Role.java
new file mode 100644
index 0000000..932a43b
--- /dev/null
+++ b/api/src/com/cloud/acl/Role.java
@@ -0,0 +1,20 @@
+package com.cloud.acl;
+
+//metadata - consists of default dynamic roles in CS + any custom roles added by user
+public interface Role {
+
+    public static final short ROOT_ADMIN = 0;
+    public static final short DOMAIN_ADMIN = 1;
+    public static final short DOMAIN_USER = 2;
+    public static final short OWNER = 3;
+    public static final short PARENT_DOMAIN_ADMIN = 4;
+    public static final short PARENT_DOMAIN_USER = 5;
+    public static final short CHILD_DOMAIN_ADMIN = 6;
+    public static final short CHILD_DOMAIN_USER = 7;
+    
+    public long getId();
+    
+    public short getRoleType();
+    
+    
+ }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/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
index 1f376e9..bd02c06 100644
--- a/api/src/com/cloud/api/ACL.java
+++ b/api/src/com/cloud/api/ACL.java
@@ -26,6 +26,6 @@ import java.lang.annotation.Target;
 @Target({ FIELD })
 public @interface ACL {
 	
-
-	Class<?> resourceType();
+	boolean checkKeyAccess() default false;
+	boolean checkValueAccess() default false;
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/api/Parameter.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/Parameter.java b/api/src/com/cloud/api/Parameter.java
index 2da3c40..e356369 100644
--- a/api/src/com/cloud/api/Parameter.java
+++ b/api/src/com/cloud/api/Parameter.java
@@ -44,4 +44,8 @@ public @interface Parameter {
     int length() default 255;
 
     String since() default "";
+    
+    Class<?>[] resourceType() default Object.class;
+    
+    String retrieveMethod() default "getById";
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/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 da9f3ea..3d00849 100644
--- a/api/src/com/cloud/api/commands/DeployVMCmd.java
+++ b/api/src/com/cloud/api/commands/DeployVMCmd.java
@@ -46,6 +46,7 @@ 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.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.security.SecurityGroup;
 import com.cloud.offering.DiskOffering;
@@ -55,6 +56,7 @@ import com.cloud.user.Account;
 import com.cloud.user.UserContext;
 import com.cloud.uservm.UserVm;
 
+
 @Implementation(description="Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject=UserVmResponse.class)
 public class DeployVMCmd extends BaseAsyncCreateCmd {
     public static final Logger s_logger = Logger.getLogger(DeployVMCmd.class.getName());
@@ -69,13 +71,14 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="availability zone for the virtual machine")
     private Long zoneId;
 
+    @ACL
     @IdentityMapper(entityTableName="disk_offering")
-    @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine")
+    @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine", resourceType=ServiceOffering.class)
     private Long serviceOfferingId;
 
-    @ACL(resourceType=VirtualMachineTemplate.class)
+    @ACL
     @IdentityMapper(entityTableName="vm_template")
-    @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine")
+    @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine",resourceType=VirtualMachineTemplate.class)
     private Long templateId;
 
     @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="host name for the virtual machine")
@@ -89,18 +92,19 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     private String accountName;
 
     @IdentityMapper(entityTableName="domain")
-    @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.")
+    @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.", entityType=Domain.class)
     private Long domainId;
 
     //Network information
-    @ACL(resourceType=Network.class)
+    @ACL
     @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")
+    @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", resourceType=Network.class)
     private List<Long> networkIds;
 
     //DataDisk information
+    @ACL
     @IdentityMapper(entityTableName="disk_offering")
-    @Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.")
+    @Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.", resourceType=DiskOffering.class)
     private Long diskOfferingId;
 
     @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId")
@@ -118,21 +122,22 @@ 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)
+    @ACL
     @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")
+    @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", resourceType=SecurityGroup.class)
     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")
+    @ACL
+    @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", resourceType=SecurityGroup.class)
     private List<String> securityGroupNameList;
     
-    @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter. Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=204 - requests to use ip 10.10.10.11 in network id=204")
+    @ACL(checkKeyAccess=true)
+    @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter. Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=204 - requests to use ip 10.10.10.11 in network id=204",resourceType={Network.class,IpAddress.class})
     private Map ipToNetworkList;
     
     @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="the ip address for default vm's network")

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/domain/Domain.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/domain/Domain.java b/api/src/com/cloud/domain/Domain.java
index a7b4031..8884749 100644
--- a/api/src/com/cloud/domain/Domain.java
+++ b/api/src/com/cloud/domain/Domain.java
@@ -23,6 +23,7 @@ import com.cloud.user.OwnedBy;
 /**
  * Domain defines the Domain object.
  */
+
 public interface Domain extends OwnedBy {
     public static final long ROOT_DOMAIN = 1L;
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/network/security/SecurityGroup.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/security/SecurityGroup.java b/api/src/com/cloud/network/security/SecurityGroup.java
index 6846740..db9d043 100644
--- a/api/src/com/cloud/network/security/SecurityGroup.java
+++ b/api/src/com/cloud/network/security/SecurityGroup.java
@@ -18,6 +18,7 @@ package com.cloud.network.security;
 
 import com.cloud.acl.ControlledEntity;
 
+@doc("")
 public interface SecurityGroup extends ControlledEntity {
     long getId();
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/api/src/com/cloud/user/Account.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/user/Account.java b/api/src/com/cloud/user/Account.java
index 18f585b..fc45698 100755
--- a/api/src/com/cloud/user/Account.java
+++ b/api/src/com/cloud/user/Account.java
@@ -34,6 +34,7 @@ public interface Account extends ControlledEntity {
         enabled,
         locked
     }
+    
 
     public static final short ACCOUNT_TYPE_NORMAL = 0;
     public static final short ACCOUNT_TYPE_ADMIN = 1;
@@ -61,4 +62,5 @@ public interface Account extends ControlledEntity {
     public String getNetworkDomain();
     
     public Long getDefaultZoneId();
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/server/src/com/cloud/acl/StaticRoleBasedAPIAccessChecker.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/acl/StaticRoleBasedAPIAccessChecker.java b/server/src/com/cloud/acl/StaticRoleBasedAPIAccessChecker.java
new file mode 100644
index 0000000..73a2646
--- /dev/null
+++ b/server/src/com/cloud/acl/StaticRoleBasedAPIAccessChecker.java
@@ -0,0 +1,188 @@
+package com.cloud.acl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.server.ManagementServer;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.User;
+import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.Inject;
+import com.cloud.utils.component.PluggableService;
+
+/*
+ * This is the default API access checker that grab's the user's account
+ * based on the account type, access is granted referring to commands in all *.properties files.
+ */
+
+@Local(value=APIAccessChecker.class)
+public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker {
+
+	protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class);
+    public static final short ADMIN_COMMAND = 1;
+    public static final short DOMAIN_ADMIN_COMMAND = 4;
+    public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2;
+    public static final short USER_COMMAND = 8;
+    private static List<String> s_userCommands = null;
+    private static List<String> s_resellerCommands = null; // AKA domain-admin
+    private static List<String> s_adminCommands = null;
+    private static List<String> s_resourceDomainAdminCommands = null;
+    private static List<String> s_allCommands = null;
+    private static List<String> s_pluggableServiceCommands = null;
+    
+    protected @Inject AccountManager _accountMgr;
+
+    static {
+    	s_allCommands = new ArrayList<String>();
+    	s_userCommands = new ArrayList<String>();
+        s_resellerCommands = new ArrayList<String>();
+        s_adminCommands = new ArrayList<String>();
+        s_resourceDomainAdminCommands = new ArrayList<String>();
+        s_pluggableServiceCommands = new ArrayList<String>();
+    }
+
+	@Override
+	public boolean canAccessAPI(User user, String apiCommandName)
+			throws PermissionDeniedException{
+	
+		boolean commandExists = s_allCommands.contains(apiCommandName);
+
+		if(commandExists && user != null){
+				Long accountId = user.getAccountId();
+		        Account userAccount = _accountMgr.getAccount(accountId);
+		        short accountType = userAccount.getType();
+		        return isCommandAvailableForAccount(accountType, apiCommandName);
+		}
+		
+		return commandExists;
+	}
+
+   private static boolean isCommandAvailableForAccount(short accountType, String commandName) {
+        boolean isCommandAvailable = false;
+        switch (accountType) {
+        case Account.ACCOUNT_TYPE_ADMIN:
+            isCommandAvailable = s_adminCommands.contains(commandName);
+            break;
+        case Account.ACCOUNT_TYPE_DOMAIN_ADMIN:
+            isCommandAvailable = s_resellerCommands.contains(commandName);
+            break;
+        case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN:
+            isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName);
+            break;
+        case Account.ACCOUNT_TYPE_NORMAL:
+            isCommandAvailable = s_userCommands.contains(commandName);
+            break;
+        }
+        return isCommandAvailable;
+    }
+
+	
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+    	super.configure(name, params);
+    	
+    	//load command.properties to build the static map per role.
+        ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
+        String[] apiConfig = ((ManagementServer) ComponentLocator.getComponent(ManagementServer.Name)).getApiConfig();
+
+        processConfigFiles(apiConfig, false);
+        
+        // get commands for all pluggable services
+        String[] pluggableServicesApiConfigs = getPluggableServicesApiConfigs();
+        processConfigFiles(pluggableServicesApiConfigs, true);
+        
+    	return true;
+    }
+    
+
+    private String[] getPluggableServicesApiConfigs() {
+        List<String> pluggableServicesApiConfigs = new ArrayList<String>();
+
+        ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
+        List<PluggableService> services = locator.getAllPluggableServices();
+        for (PluggableService service : services) {
+            pluggableServicesApiConfigs.add(service.getPropertiesFile());
+        }
+        return pluggableServicesApiConfigs.toArray(new String[0]);
+    }
+    
+    private void processConfigFiles(String[] apiConfig, boolean pluggableServicesConfig) {
+		try {
+            Properties preProcessedCommands = new Properties();
+            if (apiConfig != null) {
+                for (String configFile : apiConfig) {
+                    File commandsFile = PropertiesUtil.findConfigFile(configFile);
+                    if (commandsFile != null) {
+                        try {
+                            preProcessedCommands.load(new FileInputStream(commandsFile));
+                        } catch (FileNotFoundException fnfex) {
+                            // in case of a file within a jar in classpath, try to open stream using url
+                            InputStream stream = PropertiesUtil.openStreamFromURL(configFile);
+                            if (stream != null) {
+                                preProcessedCommands.load(stream);
+                            } else {
+                                s_logger.error("Unable to find properites file", fnfex);
+                            }
+                        }
+                    }
+                }
+                for (Object key : preProcessedCommands.keySet()) {
+                    String preProcessedCommand = preProcessedCommands.getProperty((String) key);
+                    String[] commandParts = preProcessedCommand.split(";");
+
+                    
+                    if (pluggableServicesConfig) {
+                        s_pluggableServiceCommands.add(commandParts[0]);
+                    }
+                    
+                    if (commandParts.length > 1) {
+                        try {
+                            short cmdPermissions = Short.parseShort(commandParts[1]);
+                            if ((cmdPermissions & ADMIN_COMMAND) != 0) {
+                                s_adminCommands.add((String) key);
+                            }
+                            if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) {
+                                s_resourceDomainAdminCommands.add((String) key);
+                            }
+                            if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) {
+                                s_resellerCommands.add((String) key);
+                            }
+                            if ((cmdPermissions & USER_COMMAND) != 0) {
+                                s_userCommands.add((String) key);
+                            }
+                            s_allCommands.addAll(s_adminCommands);
+                            s_allCommands.addAll(s_resourceDomainAdminCommands);
+                            s_allCommands.addAll(s_userCommands);
+                            s_allCommands.addAll(s_resellerCommands);
+                        } catch (NumberFormatException nfe) {
+                            s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand);
+                        }
+                    }
+                }
+
+            }
+        } catch (FileNotFoundException fnfex) {
+            s_logger.error("Unable to find properites file", fnfex);
+        } catch (IOException ioex) {
+            s_logger.error("Exception loading properties file", ioex);
+        }
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/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 de7bdc6..d249d6e 100755
--- a/server/src/com/cloud/api/ApiDispatcher.java
+++ b/server/src/com/cloud/api/ApiDispatcher.java
@@ -31,6 +31,7 @@ import java.util.regex.Matcher;
 import org.apache.log4j.Logger;
 
 import com.cloud.acl.ControlledEntity;
+import com.cloud.acl.Role;
 import com.cloud.api.BaseCmd.CommandType;
 import com.cloud.api.commands.ListEventsCmd;
 import com.cloud.async.AsyncCommandQueued;
@@ -93,17 +94,8 @@ public class ApiDispatcher {
 
     	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);
-        }
+        doAccessChecks(cmd, entitiesToAccess);
         
         try {
             UserContext ctx = UserContext.current();
@@ -144,10 +136,54 @@ public class ApiDispatcher {
         }
     }
 
-    public void dispatch(BaseCmd cmd, Map<String, String> params) {
+    private void doAccessChecks(BaseAsyncCreateCmd cmd,
+			List<ControlledEntity> entitiesToAccess) {
+
+		//owner
+		Account caller = UserContext.current().getCaller();
+		Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+
+		List<Role> callerRoles = determineRole(caller);
+		
+		List<Role> ownerRoles = determineRole(owner);
+		
+		//check permission to call this command for the caller
+		//this needs checking of static roles of the caller
+		checkACLOnCommand(cmd);
+		
+		//check that caller can access the owner account.
+		s_instance._accountMgr.checkAccess(caller, null, true, owner);
+		
+		checkACLOnEntities(caller, entitiesToAccess);
+
+	}
+    
+    
+    private void checkACLOnCommand(BaseAsyncCreateCmd cmd) {
+		// TODO Auto-generated method stub
+		//need to write an commandACLChecker adapter framework to check ACL on commands - default one will use the static roles by referring to commands.properties.
+    	//one can write another commandACLChecker to check access via custom roles.
+	}
+
+	private List<Role> determineRole(Account caller) {
+		// TODO Auto-generated method stub
+		List<Role> effectiveRoles = new ArrayList<Role>();
+		return effectiveRoles;
+		
+	}
+
+	private void checkACLOnEntities(Account caller, List<ControlledEntity> entitiesToAccess){
+		//checkACLOnEntities
+    	if(!entitiesToAccess.isEmpty()){
+			for(ControlledEntity entity : entitiesToAccess)
+			s_instance._accountMgr.checkAccess(caller, null, true, entity);
+       }
+
+    }
+
+	public void dispatch(BaseCmd cmd, Map<String, String> params) {
     	List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
     	setupParameters(cmd, params, entitiesToAccess);
-        ApiDispatcher.plugService(cmd);
         
         if(!entitiesToAccess.isEmpty()){
 			 //owner
@@ -343,6 +379,13 @@ public class ApiDispatcher {
         }
 
         for (Field field : fields) {
+        	
+        	//plug Services
+        	PlugService plugServiceAnnotation = field.getAnnotation(PlugService.class);
+        	if(plugServiceAnnotation != null){
+        		plugService(field, cmd);
+        	}
+        	
             Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
             if ((parameterAnnotation == null) || !parameterAnnotation.expose()) {
                 continue;
@@ -395,8 +438,13 @@ public class ApiDispatcher {
 	            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();
+	            	
+	            	//parse the array of resource types and in case of map check access on key or value or both as specified in @acl
+	            	//implement external dao for classes that need findByName
+	            	//for maps, specify access to be checkd on key or value.
+	            	
+	            	if(parameterAnnotation.resourceType() != null){
+	            		 Class<?>[] entity = parameterAnnotation.resourceType();
 	            				 
 	            		 if(ControlledEntity.class.isAssignableFrom(entity)){
 	                         if (s_logger.isDebugEnabled()) {
@@ -584,43 +632,29 @@ public class ApiDispatcher {
         return cal.getTime();
     }
 
-    public static void plugService(BaseCmd cmd) {
+    public static void plugService(Field field, BaseCmd cmd) {
+        ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
 
-        if (!ApiServer.isPluggableServiceCommand(cmd.getClass().getName())) {
-            return;
+        Class<?> fc = field.getType();
+        Object instance = null;
+        if (PluggableService.class.isAssignableFrom(fc)) {
+            instance = locator.getPluggableService(fc);
         }
-        Class<?> clazz = cmd.getClass();
-        ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
-        do {
-            Field[] fields = clazz.getDeclaredFields();
-            for (Field field : fields) {
-                PlugService plugService = field.getAnnotation(PlugService.class);
-                if (plugService == null) {
-                    continue;
-                }
-                Class<?> fc = field.getType();
-                Object instance = null;
-                if (PluggableService.class.isAssignableFrom(fc)) {
-                    instance = locator.getPluggableService(fc);
-                }
 
-                if (instance == null) {
-                    throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + clazz.getSimpleName());
-                }
+        if (instance == null) {
+            throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + cmd.getClass().getSimpleName());
+        }
 
-                try {
-                    field.setAccessible(true);
-                    field.set(cmd, instance);
-                } catch (IllegalArgumentException e) {
-                    s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName());
-                    throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]");
-                } catch (IllegalAccessException e) {
-                    s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
-                    throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
-                }
-            }
-            clazz = clazz.getSuperclass();
-        } while (clazz != Object.class && clazz != null);
+        try {
+            field.setAccessible(true);
+            field.set(cmd, instance);
+        } catch (IllegalArgumentException e) {
+            s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName());
+            throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]");
+        } catch (IllegalAccessException e) {
+            s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
+            throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
+        }
     }
     
     

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/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 3186d95..28b13aa 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -82,6 +82,7 @@ import org.apache.http.protocol.ResponseServer;
 import org.apache.log4j.Logger;
 
 import com.cloud.acl.ControlledEntity;
+import com.cloud.agent.manager.allocator.HostAllocator;
 import com.cloud.api.response.ApiResponseSerializer;
 import com.cloud.api.response.ExceptionResponse;
 import com.cloud.api.response.ListResponse;
@@ -109,22 +110,21 @@ import com.cloud.user.UserVO;
 import com.cloud.utils.IdentityProxy;
 import com.cloud.utils.Pair;
 import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.component.Adapters;
 import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.PluggableService;
 import com.cloud.utils.concurrency.NamedThreadFactory;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CSExceptionErrorCode;
 import com.cloud.uuididentity.dao.IdentityDao;
+import com.cloud.acl.APIAccessChecker;
 
 public class ApiServer implements HttpRequestHandler {
     private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName());
     private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName());
 
-    public static final short ADMIN_COMMAND = 1;
-    public static final short DOMAIN_ADMIN_COMMAND = 4;
-    public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2;
-    public static final short USER_COMMAND = 8;
     public static boolean encodeApiResponse = false;
     public static String jsonContentType = "text/javascript";
     private Properties _apiCommands = null;
@@ -134,28 +134,14 @@ public class ApiServer implements HttpRequestHandler {
     private AsyncJobManager _asyncMgr = null;
     private Account _systemAccount = null;
     private User _systemUser = null;
-
     private static int _workerCount = 0;
-
     private static ApiServer s_instance = null;
-    private static List<String> s_userCommands = null;
-    private static List<String> s_resellerCommands = null; // AKA domain-admin
-    private static List<String> s_adminCommands = null;
-    private static List<String> s_resourceDomainAdminCommands = null;
-    private static List<String> s_allCommands = null;
-    private static List<String> s_pluggableServiceCommands = null;
     private static final DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
-
+    @Inject(adapter = APIAccessChecker.class)
+    protected Adapters<APIAccessChecker> _apiAccessCheckers;
+    
     private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory("ApiServer"));
 
-    static {
-        s_userCommands = new ArrayList<String>();
-        s_resellerCommands = new ArrayList<String>();
-        s_adminCommands = new ArrayList<String>();
-        s_resourceDomainAdminCommands = new ArrayList<String>();
-        s_allCommands = new ArrayList<String>();
-        s_pluggableServiceCommands = new ArrayList<String>();
-    }
 
     private ApiServer() {
     }
@@ -172,103 +158,10 @@ public class ApiServer implements HttpRequestHandler {
         return s_instance;
     }
 
-    public Properties get_apiCommands() {
-        return _apiCommands;
-    }
-
-    public static boolean isPluggableServiceCommand(String cmdClassName) {
-        if (s_pluggableServiceCommands != null) {
-            if (s_pluggableServiceCommands.contains(cmdClassName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private String[] getPluggableServicesApiConfigs() {
-        List<String> pluggableServicesApiConfigs = new ArrayList<String>();
-
-        ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
-        List<PluggableService> services = locator.getAllPluggableServices();
-        for (PluggableService service : services) {
-            pluggableServicesApiConfigs.add(service.getPropertiesFile());
-        }
-        return pluggableServicesApiConfigs.toArray(new String[0]);
-    }
-
-    private void processConfigFiles(String[] apiConfig, boolean pluggableServicesConfig) {
-        try {
-            if (_apiCommands == null) {
-                _apiCommands = new Properties();
-            }
-            Properties preProcessedCommands = new Properties();
-            if (apiConfig != null) {
-                for (String configFile : apiConfig) {
-                    File commandsFile = PropertiesUtil.findConfigFile(configFile);
-                    if (commandsFile != null) {
-                        try {
-                            preProcessedCommands.load(new FileInputStream(commandsFile));
-                        } catch (FileNotFoundException fnfex) {
-                            // in case of a file within a jar in classpath, try to open stream using url
-                            InputStream stream = PropertiesUtil.openStreamFromURL(configFile);
-                            if (stream != null) {
-                                preProcessedCommands.load(stream);
-                            } else {
-                                s_logger.error("Unable to find properites file", fnfex);
-                            }
-                        }
-                    }
-                }
-                for (Object key : preProcessedCommands.keySet()) {
-                    String preProcessedCommand = preProcessedCommands.getProperty((String) key);
-                    String[] commandParts = preProcessedCommand.split(";");
-                    _apiCommands.put(key, commandParts[0]);
-
-                    if (pluggableServicesConfig) {
-                        s_pluggableServiceCommands.add(commandParts[0]);
-                    }
-
-                    if (commandParts.length > 1) {
-                        try {
-                            short cmdPermissions = Short.parseShort(commandParts[1]);
-                            if ((cmdPermissions & ADMIN_COMMAND) != 0) {
-                                s_adminCommands.add((String) key);
-                            }
-                            if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) {
-                                s_resourceDomainAdminCommands.add((String) key);
-                            }
-                            if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) {
-                                s_resellerCommands.add((String) key);
-                            }
-                            if ((cmdPermissions & USER_COMMAND) != 0) {
-                                s_userCommands.add((String) key);
-                            }
-                        } catch (NumberFormatException nfe) {
-                            s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand);
-                        }
-                    }
-                }
-
-                s_allCommands.addAll(s_adminCommands);
-                s_allCommands.addAll(s_resourceDomainAdminCommands);
-                s_allCommands.addAll(s_userCommands);
-                s_allCommands.addAll(s_resellerCommands);
-            }
-        } catch (FileNotFoundException fnfex) {
-            s_logger.error("Unable to find properites file", fnfex);
-        } catch (IOException ioex) {
-            s_logger.error("Exception loading properties file", ioex);
-        }
-    }
 
     public void init(String[] apiConfig) {
         BaseCmd.setComponents(new ApiResponseHelper());
         BaseListCmd.configure();
-        processConfigFiles(apiConfig, false);
-
-        // get commands for all pluggable services
-        String[] pluggableServicesApiConfigs = getPluggableServicesApiConfigs();
-        processConfigFiles(pluggableServicesApiConfigs, true);
 
         ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
         _accountMgr = locator.getManager(AccountManager.class);
@@ -489,7 +382,6 @@ public class ApiServer implements HttpRequestHandler {
             } else {
             	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());
@@ -614,11 +506,6 @@ public class ApiServer implements HttpRequestHandler {
          */
     }
 
-    private static boolean isCommandAvailable(String commandName) {
-        boolean isCommandAvailable = false;
-        isCommandAvailable = s_allCommands.contains(commandName);
-        return isCommandAvailable;
-    }
 
     public boolean verifyRequest(Map<String, Object[]> requestParameters, Long userId) throws ServerApiException {
         try {
@@ -637,18 +524,15 @@ public class ApiServer implements HttpRequestHandler {
 
             // if userId not null, that mean that user is logged in
             if (userId != null) {
-                Long accountId = ApiDBUtils.findUserById(userId).getAccountId();
-                Account userAccount = _accountMgr.getAccount(accountId);
-                short accountType = userAccount.getType();
-
-                if (!isCommandAvailable(accountType, commandName)) {
+            	User user = ApiDBUtils.findUserById(userId);
+                if (!isCommandAvailable(user, commandName)) {
                     s_logger.warn("The given command:" + commandName + " does not exist");
                     throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist");
                 }
                 return true;
             } else {
                 // check against every available command to see if the command exists or not
-                if (!isCommandAvailable(commandName) && !commandName.equals("login") && !commandName.equals("logout")) {
+                if (!isCommandAvailable(null, commandName) && !commandName.equals("login") && !commandName.equals("logout")) {
                     s_logger.warn("The given command:" + commandName + " does not exist");
                     throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist");
                 }
@@ -742,7 +626,7 @@ public class ApiServer implements HttpRequestHandler {
 
             UserContext.updateContext(user.getId(), account, null);
 
-            if (!isCommandAvailable(account.getType(), commandName)) {
+            if (!isCommandAvailable(user, commandName)) {
                 s_logger.warn("The given command:" + commandName + " does not exist");
                 throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist");
             }
@@ -880,25 +764,16 @@ public class ApiServer implements HttpRequestHandler {
         return true;
     }
 
-    public static boolean isCommandAvailable(short accountType, String commandName) {
+    private boolean isCommandAvailable(User user, String commandName) {
         boolean isCommandAvailable = false;
-        switch (accountType) {
-        case Account.ACCOUNT_TYPE_ADMIN:
-            isCommandAvailable = s_adminCommands.contains(commandName);
-            break;
-        case Account.ACCOUNT_TYPE_DOMAIN_ADMIN:
-            isCommandAvailable = s_resellerCommands.contains(commandName);
-            break;
-        case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN:
-            isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName);
-            break;
-        case Account.ACCOUNT_TYPE_NORMAL:
-            isCommandAvailable = s_userCommands.contains(commandName);
-            break;
+        
+        for(APIAccessChecker apichecker : _apiAccessCheckers){
+        	isCommandAvailable = apichecker.canAccessAPI(user, commandName);
         }
+        
         return isCommandAvailable;
     }
-
+    
     // FIXME: rather than isError, we might was to pass in the status code to give more flexibility
     private void writeResponse(HttpResponse resp, final String responseText, final int statusCode, String responseType, String reasonPhrase) {
         try {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/dbbe96c9/server/src/com/cloud/host/dao/HostDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/host/dao/HostDaoImpl.java b/server/src/com/cloud/host/dao/HostDaoImpl.java
index ab1e77e..05d5b4b 100755
--- a/server/src/com/cloud/host/dao/HostDaoImpl.java
+++ b/server/src/com/cloud/host/dao/HostDaoImpl.java
@@ -63,7 +63,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
 @Local(value = { HostDao.class })
 @DB(txn = false)
 @TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1)
-public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao {
+public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao, ExternalIdDao {
     private static final Logger s_logger = Logger.getLogger(HostDaoImpl.class);
     private static final Logger status_logger = Logger.getLogger(Status.class);
     private static final Logger state_logger = Logger.getLogger(ResourceState.class);