You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pr...@apache.org on 2012/11/30 01:10:41 UTC
[2/2] git commit: Some ACL POC work
Some ACL POC work
Conflicts:
server/src/com/cloud/api/ApiDispatcher.java
Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/07386324
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/07386324
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/07386324
Branch: refs/heads/api_refactoring
Commit: 073863249abf36b6879359889f5731984391fa41
Parents: a526460
Author: Prachi Damle <pr...@cloud.com>
Authored: Thu Nov 29 16:09:47 2012 -0800
Committer: Prachi Damle <pr...@cloud.com>
Committed: Thu Nov 29 16:09:47 2012 -0800
----------------------------------------------------------------------
api/src/com/cloud/api/ACL.java | 31 +++++
api/src/com/cloud/api/commands/DeployVMCmd.java | 9 ++
server/src/com/cloud/api/ApiDispatcher.java | 127 +++++++++++++++++-
server/src/com/cloud/api/ApiServer.java | 11 ++-
4 files changed, 173 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/api/src/com/cloud/api/ACL.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/ACL.java b/api/src/com/cloud/api/ACL.java
new file mode 100644
index 0000000..1f376e9
--- /dev/null
+++ b/api/src/com/cloud/api/ACL.java
@@ -0,0 +1,31 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.api;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ FIELD })
+public @interface ACL {
+
+
+ Class<?> resourceType();
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/api/src/com/cloud/api/commands/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java
index f67ee8f..da9f3ea 100644
--- a/api/src/com/cloud/api/commands/DeployVMCmd.java
+++ b/api/src/com/cloud/api/commands/DeployVMCmd.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.apache.log4j.Logger;
+import com.cloud.api.ACL;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseAsyncCreateCmd;
import com.cloud.api.BaseCmd;
@@ -43,7 +44,10 @@ import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.Network;
+import com.cloud.network.security.SecurityGroup;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;
@@ -69,6 +73,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
@Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine")
private Long serviceOfferingId;
+ @ACL(resourceType=VirtualMachineTemplate.class)
@IdentityMapper(entityTableName="vm_template")
@Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine")
private Long templateId;
@@ -88,6 +93,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
private Long domainId;
//Network information
+ @ACL(resourceType=Network.class)
@IdentityMapper(entityTableName="networks")
@Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter")
private List<Long> networkIds;
@@ -112,14 +118,17 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
@Parameter(name=ApiConstants.SSH_KEYPAIR, type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine")
private String sshKeyPairName;
+ //@ACL(resourceType=Host.class)
@IdentityMapper(entityTableName="host")
@Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, description="destination Host ID to deploy the VM to - parameter available for root admin only")
private Long hostId;
+ //@ACL(resourceType=SecurityGroup.class)
@IdentityMapper(entityTableName="security_group")
@Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter")
private List<Long> securityGroupIdList;
+ //@ACL(resourceType=SecurityGroup.class)
@Parameter(name=ApiConstants.SECURITY_GROUP_NAMES, type=CommandType.LIST, collectionType=CommandType.STRING, description="comma separated list of security groups names that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter")
private List<String> securityGroupNameList;
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/server/src/com/cloud/api/ApiDispatcher.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java
index dfe4a1f..471dfc4 100755
--- a/server/src/com/cloud/api/ApiDispatcher.java
+++ b/server/src/com/cloud/api/ApiDispatcher.java
@@ -22,6 +22,7 @@ import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -29,6 +30,7 @@ import java.util.regex.Matcher;
import org.apache.log4j.Logger;
+import com.cloud.acl.ControlledEntity;
import com.cloud.api.BaseCmd.CommandType;
import com.cloud.api.commands.ListEventsCmd;
import com.cloud.async.AsyncCommandQueued;
@@ -42,14 +44,19 @@ import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.utils.IdentityProxy;
+import com.cloud.network.dao.NetworkDao;
import com.cloud.server.ManagementServer;
+import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountService;
import com.cloud.user.UserContext;
import com.cloud.utils.DateUtil;
import com.cloud.utils.IdentityProxy;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.db.GenericDao;
import com.cloud.utils.exception.CSExceptionErrorCode;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.uuididentity.dao.IdentityDao;
@@ -64,7 +71,10 @@ public class ApiDispatcher {
AsyncJobManager _asyncMgr;
IdentityDao _identityDao;
Long _createSnapshotQueueSizeLimit;
+ AccountManager _accountMgr;
+
+ Map<String, Class<? extends GenericDao>> _daoNameMap = new HashMap<String, Class<? extends GenericDao>>();
// singleton class
private static ApiDispatcher s_instance = new ApiDispatcher();
@@ -76,6 +86,7 @@ public class ApiDispatcher {
_locator = ComponentLocator.getLocator(ManagementServer.Name);
_asyncMgr = _locator.getManager(AsyncJobManager.class);
_identityDao = _locator.getDao(IdentityDao.class);
+
ConfigurationDao configDao = _locator.getDao(ConfigurationDao.class);
Map<String, String> configs = configDao.getConfiguration();
String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key());
@@ -88,13 +99,30 @@ public class ApiDispatcher {
_createSnapshotQueueSizeLimit = snapshotLimit;
}
}
+ _accountMgr = _locator.getManager(AccountManager.class);
+
+ _daoNameMap.put("com.cloud.network.Network", NetworkDao.class);
+ _daoNameMap.put("com.cloud.template.VirtualMachineTemplate", VMTemplateDao.class);
+
+
}
public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map<String, String> params) {
- setupParameters(cmd, params);
+ List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+ setupParameters(cmd, params, entitiesToAccess);
plugService(cmd);
+ if(!entitiesToAccess.isEmpty()){
+ //owner
+ Account caller = UserContext.current().getCaller();
+ Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+ s_instance._accountMgr.checkAccess(caller, null, true, owner);
+
+ for(ControlledEntity entity : entitiesToAccess)
+ s_instance._accountMgr.checkAccess(caller, null, true, entity);
+ }
+
try {
UserContext ctx = UserContext.current();
ctx.setAccountId(cmd.getEntityOwnerId());
@@ -135,8 +163,19 @@ public class ApiDispatcher {
}
public void dispatch(BaseCmd cmd, Map<String, String> params) {
- setupParameters(cmd, params);
+ List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+ setupParameters(cmd, params, entitiesToAccess);
ApiDispatcher.plugService(cmd);
+
+ if(!entitiesToAccess.isEmpty()){
+ //owner
+ Account caller = UserContext.current().getCaller();
+ Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+ s_instance._accountMgr.checkAccess(caller, null, true, owner);
+ for(ControlledEntity entity : entitiesToAccess)
+ s_instance._accountMgr.checkAccess(caller, null, true, entity);
+ }
+
try {
UserContext ctx = UserContext.current();
ctx.setAccountId(cmd.getEntityOwnerId());
@@ -299,8 +338,10 @@ public class ApiDispatcher {
}
}
- public static void setupParameters(BaseCmd cmd, Map<String, String> params) {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static void setupParameters(BaseCmd cmd, Map<String, String> params, List<ControlledEntity> entitiesToAccess) {
Map<String, Object> unpackedParams = cmd.unpackParams(params);
+
if (cmd instanceof BaseListCmd) {
Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE);
@@ -368,12 +409,90 @@ public class ApiDispatcher {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage());
} catch (CloudRuntimeException cloudEx) {
// FIXME: Better error message? This only happens if the API command is not executable, which typically
-// means
+ //means
// there was
// and IllegalAccessException setting one of the parameters.
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
}
+
+
+ //check access on the resource this field points to
+ try {
+ ACL checkAccess = field.getAnnotation(ACL.class);
+ CommandType fieldType = parameterAnnotation.type();
+
+
+ if(checkAccess != null){
+ // Verify that caller can perform actions in behalf of vm owner
+ //acumulate all Controlled Entities together.
+ if(checkAccess.resourceType() != null){
+ Class<?> entity = checkAccess.resourceType();
+
+ if(ControlledEntity.class.isAssignableFrom(entity)){
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("entity name is:" + entity.getName());
+ }
+
+ if(s_instance._daoNameMap.containsKey(entity.getName())){
+ Class<? extends GenericDao> daoClass = s_instance._daoNameMap.get(entity.getName());
+ GenericDao daoClassInstance = s_instance._locator.getDao(daoClass);
+
+ //Check if the parameter type is a single Id or list of id's/name's
+ switch (fieldType) {
+ case LIST:
+ CommandType listType = parameterAnnotation.collectionType();
+ switch (listType) {
+ case LONG:
+ List<Long> listParam = new ArrayList<Long>();
+ listParam = (List)field.get(cmd);
+
+ for(Long entityId : listParam){
+ ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
+ entitiesToAccess.add(entityObj);
+ }
+ break;
+ /*case STRING:
+ List<String> listParam = new ArrayList<String>();
+ listParam = (List)field.get(cmd);
+ for(String entityName: listParam){
+ ControlledEntity entityObj = (ControlledEntity)daoClassInstance(entityId);
+ entitiesToAccess.add(entityObj);
+ }
+ break;
+ */
+ default:
+ break;
+ }
+ break;
+ case LONG:
+ Long entityId = (Long)field.get(cmd);
+ ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
+ entitiesToAccess.add(entityObj);
+ break;
+ default:
+ break;
+ }
+
+
+ }
+
+ }
+
+ }
+
+ }
+
+ } catch (IllegalArgumentException e) {
+ s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
+ throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
+ } catch (IllegalAccessException e) {
+ s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
+ throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
+ }
+
}
+
+ //check access on the entities.
}
@SuppressWarnings({ "unchecked", "rawtypes" })
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/07386324/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index a5c9ea5..cfd3b25 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -81,6 +81,7 @@ import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.log4j.Logger;
+import com.cloud.acl.ControlledEntity;
import com.cloud.api.response.ApiResponseSerializer;
import com.cloud.api.response.ExceptionResponse;
import com.cloud.api.response.ListResponse;
@@ -487,8 +488,16 @@ public class ApiServer implements HttpRequestHandler {
objectEntityTable = createCmd.getEntityTable();
params.put("id", objectId.toString());
} else {
- ApiDispatcher.setupParameters(cmdObj, params);
+ List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
+ ApiDispatcher.setupParameters(cmdObj, params, entitiesToAccess);
ApiDispatcher.plugService(cmdObj);
+
+ if(!entitiesToAccess.isEmpty()){
+ Account owner = s_instance._accountMgr.getActiveAccountById(cmdObj.getEntityOwnerId());
+ s_instance._accountMgr.checkAccess(caller, null, true, owner);
+
+ s_instance._accountMgr.checkAccess(caller, null, true, (ControlledEntity[])entitiesToAccess.toArray());
+ }
}
BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj;