mirror of https://github.com/apache/cloudstack.git
Some ACL POC work
Conflicts: server/src/com/cloud/api/ApiDispatcher.java
This commit is contained in:
parent
a526460195
commit
073863249a
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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" })
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue