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 <[email protected]>
Authored: Tue Nov 13 11:47:38 2012 -0800
Committer: Prachi Damle <[email protected]>
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);

Reply via email to