Skip to content

Commit 0738632

Browse files
author
Prachi Damle
committed
Some ACL POC work
Conflicts: server/src/com/cloud/api/ApiDispatcher.java
1 parent a526460 commit 0738632

4 files changed

Lines changed: 173 additions & 5 deletions

File tree

api/src/com/cloud/api/ACL.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.api;
18+
19+
import static java.lang.annotation.ElementType.FIELD;
20+
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
@Retention(RetentionPolicy.RUNTIME)
26+
@Target({ FIELD })
27+
public @interface ACL {
28+
29+
30+
Class<?> resourceType();
31+
}

api/src/com/cloud/api/commands/DeployVMCmd.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.apache.log4j.Logger;
2828

29+
import com.cloud.api.ACL;
2930
import com.cloud.api.ApiConstants;
3031
import com.cloud.api.BaseAsyncCreateCmd;
3132
import com.cloud.api.BaseCmd;
@@ -43,7 +44,10 @@
4344
import com.cloud.exception.InvalidParameterValueException;
4445
import com.cloud.exception.ResourceAllocationException;
4546
import com.cloud.exception.ResourceUnavailableException;
47+
import com.cloud.host.Host;
4648
import com.cloud.hypervisor.Hypervisor.HypervisorType;
49+
import com.cloud.network.Network;
50+
import com.cloud.network.security.SecurityGroup;
4751
import com.cloud.offering.DiskOffering;
4852
import com.cloud.offering.ServiceOffering;
4953
import com.cloud.template.VirtualMachineTemplate;
@@ -69,6 +73,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
6973
@Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine")
7074
private Long serviceOfferingId;
7175

76+
@ACL(resourceType=VirtualMachineTemplate.class)
7277
@IdentityMapper(entityTableName="vm_template")
7378
@Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine")
7479
private Long templateId;
@@ -88,6 +93,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
8893
private Long domainId;
8994

9095
//Network information
96+
@ACL(resourceType=Network.class)
9197
@IdentityMapper(entityTableName="networks")
9298
@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")
9399
private List<Long> networkIds;
@@ -112,14 +118,17 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
112118
@Parameter(name=ApiConstants.SSH_KEYPAIR, type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine")
113119
private String sshKeyPairName;
114120

121+
//@ACL(resourceType=Host.class)
115122
@IdentityMapper(entityTableName="host")
116123
@Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, description="destination Host ID to deploy the VM to - parameter available for root admin only")
117124
private Long hostId;
118125

126+
//@ACL(resourceType=SecurityGroup.class)
119127
@IdentityMapper(entityTableName="security_group")
120128
@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")
121129
private List<Long> securityGroupIdList;
122130

131+
//@ACL(resourceType=SecurityGroup.class)
123132
@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")
124133
private List<String> securityGroupNameList;
125134

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

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@
2222
import java.util.ArrayList;
2323
import java.util.Calendar;
2424
import java.util.Date;
25+
import java.util.HashMap;
2526
import java.util.List;
2627
import java.util.Map;
2728
import java.util.StringTokenizer;
2829
import java.util.regex.Matcher;
2930

3031
import org.apache.log4j.Logger;
3132

33+
import com.cloud.acl.ControlledEntity;
3234
import com.cloud.api.BaseCmd.CommandType;
3335
import com.cloud.api.commands.ListEventsCmd;
3436
import com.cloud.async.AsyncCommandQueued;
@@ -42,14 +44,19 @@
4244
import com.cloud.exception.ResourceAllocationException;
4345
import com.cloud.exception.ResourceUnavailableException;
4446
import com.cloud.utils.IdentityProxy;
47+
import com.cloud.network.dao.NetworkDao;
4548
import com.cloud.server.ManagementServer;
49+
import com.cloud.storage.dao.VMTemplateDao;
4650
import com.cloud.user.Account;
51+
import com.cloud.user.AccountManager;
52+
import com.cloud.user.AccountService;
4753
import com.cloud.user.UserContext;
4854
import com.cloud.utils.DateUtil;
4955
import com.cloud.utils.IdentityProxy;
5056
import com.cloud.utils.NumbersUtil;
5157
import com.cloud.utils.component.ComponentLocator;
5258
import com.cloud.utils.component.PluggableService;
59+
import com.cloud.utils.db.GenericDao;
5360
import com.cloud.utils.exception.CSExceptionErrorCode;
5461
import com.cloud.utils.exception.CloudRuntimeException;
5562
import com.cloud.uuididentity.dao.IdentityDao;
@@ -64,7 +71,10 @@ public class ApiDispatcher {
6471
AsyncJobManager _asyncMgr;
6572
IdentityDao _identityDao;
6673
Long _createSnapshotQueueSizeLimit;
74+
AccountManager _accountMgr;
6775

76+
77+
Map<String, Class<? extends GenericDao>> _daoNameMap = new HashMap<String, Class<? extends GenericDao>>();
6878
// singleton class
6979
private static ApiDispatcher s_instance = new ApiDispatcher();
7080

@@ -76,6 +86,7 @@ private ApiDispatcher() {
7686
_locator = ComponentLocator.getLocator(ManagementServer.Name);
7787
_asyncMgr = _locator.getManager(AsyncJobManager.class);
7888
_identityDao = _locator.getDao(IdentityDao.class);
89+
7990
ConfigurationDao configDao = _locator.getDao(ConfigurationDao.class);
8091
Map<String, String> configs = configDao.getConfiguration();
8192
String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key());
@@ -88,13 +99,30 @@ private ApiDispatcher() {
8899
_createSnapshotQueueSizeLimit = snapshotLimit;
89100
}
90101
}
102+
_accountMgr = _locator.getManager(AccountManager.class);
103+
104+
_daoNameMap.put("com.cloud.network.Network", NetworkDao.class);
105+
_daoNameMap.put("com.cloud.template.VirtualMachineTemplate", VMTemplateDao.class);
106+
107+
91108
}
92109

93110
public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map<String, String> params) {
94111

95-
setupParameters(cmd, params);
112+
List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
113+
setupParameters(cmd, params, entitiesToAccess);
96114
plugService(cmd);
97115

116+
if(!entitiesToAccess.isEmpty()){
117+
//owner
118+
Account caller = UserContext.current().getCaller();
119+
Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
120+
s_instance._accountMgr.checkAccess(caller, null, true, owner);
121+
122+
for(ControlledEntity entity : entitiesToAccess)
123+
s_instance._accountMgr.checkAccess(caller, null, true, entity);
124+
}
125+
98126
try {
99127
UserContext ctx = UserContext.current();
100128
ctx.setAccountId(cmd.getEntityOwnerId());
@@ -135,8 +163,19 @@ public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map<String, String> params
135163
}
136164

137165
public void dispatch(BaseCmd cmd, Map<String, String> params) {
138-
setupParameters(cmd, params);
166+
List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
167+
setupParameters(cmd, params, entitiesToAccess);
139168
ApiDispatcher.plugService(cmd);
169+
170+
if(!entitiesToAccess.isEmpty()){
171+
//owner
172+
Account caller = UserContext.current().getCaller();
173+
Account owner = s_instance._accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
174+
s_instance._accountMgr.checkAccess(caller, null, true, owner);
175+
for(ControlledEntity entity : entitiesToAccess)
176+
s_instance._accountMgr.checkAccess(caller, null, true, entity);
177+
}
178+
140179
try {
141180
UserContext ctx = UserContext.current();
142181
ctx.setAccountId(cmd.getEntityOwnerId());
@@ -299,8 +338,10 @@ public void dispatch(BaseCmd cmd, Map<String, String> params) {
299338
}
300339
}
301340

302-
public static void setupParameters(BaseCmd cmd, Map<String, String> params) {
341+
@SuppressWarnings({ "unchecked", "rawtypes" })
342+
public static void setupParameters(BaseCmd cmd, Map<String, String> params, List<ControlledEntity> entitiesToAccess) {
303343
Map<String, Object> unpackedParams = cmd.unpackParams(params);
344+
304345

305346
if (cmd instanceof BaseListCmd) {
306347
Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE);
@@ -368,12 +409,90 @@ public static void setupParameters(BaseCmd cmd, Map<String, String> params) {
368409
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());
369410
} catch (CloudRuntimeException cloudEx) {
370411
// FIXME: Better error message? This only happens if the API command is not executable, which typically
371-
// means
412+
//means
372413
// there was
373414
// and IllegalAccessException setting one of the parameters.
374415
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
375416
}
417+
418+
419+
//check access on the resource this field points to
420+
try {
421+
ACL checkAccess = field.getAnnotation(ACL.class);
422+
CommandType fieldType = parameterAnnotation.type();
423+
424+
425+
if(checkAccess != null){
426+
// Verify that caller can perform actions in behalf of vm owner
427+
//acumulate all Controlled Entities together.
428+
if(checkAccess.resourceType() != null){
429+
Class<?> entity = checkAccess.resourceType();
430+
431+
if(ControlledEntity.class.isAssignableFrom(entity)){
432+
if (s_logger.isDebugEnabled()) {
433+
s_logger.debug("entity name is:" + entity.getName());
434+
}
435+
436+
if(s_instance._daoNameMap.containsKey(entity.getName())){
437+
Class<? extends GenericDao> daoClass = s_instance._daoNameMap.get(entity.getName());
438+
GenericDao daoClassInstance = s_instance._locator.getDao(daoClass);
439+
440+
//Check if the parameter type is a single Id or list of id's/name's
441+
switch (fieldType) {
442+
case LIST:
443+
CommandType listType = parameterAnnotation.collectionType();
444+
switch (listType) {
445+
case LONG:
446+
List<Long> listParam = new ArrayList<Long>();
447+
listParam = (List)field.get(cmd);
448+
449+
for(Long entityId : listParam){
450+
ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
451+
entitiesToAccess.add(entityObj);
452+
}
453+
break;
454+
/*case STRING:
455+
List<String> listParam = new ArrayList<String>();
456+
listParam = (List)field.get(cmd);
457+
for(String entityName: listParam){
458+
ControlledEntity entityObj = (ControlledEntity)daoClassInstance(entityId);
459+
entitiesToAccess.add(entityObj);
460+
}
461+
break;
462+
*/
463+
default:
464+
break;
465+
}
466+
break;
467+
case LONG:
468+
Long entityId = (Long)field.get(cmd);
469+
ControlledEntity entityObj = (ControlledEntity)daoClassInstance.findById(entityId);
470+
entitiesToAccess.add(entityObj);
471+
break;
472+
default:
473+
break;
474+
}
475+
476+
477+
}
478+
479+
}
480+
481+
}
482+
483+
}
484+
485+
} catch (IllegalArgumentException e) {
486+
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
487+
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
488+
} catch (IllegalAccessException e) {
489+
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
490+
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
491+
}
492+
376493
}
494+
495+
//check access on the entities.
377496
}
378497

379498
@SuppressWarnings({ "unchecked", "rawtypes" })

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import org.apache.http.protocol.ResponseServer;
8282
import org.apache.log4j.Logger;
8383

84+
import com.cloud.acl.ControlledEntity;
8485
import com.cloud.api.response.ApiResponseSerializer;
8586
import com.cloud.api.response.ExceptionResponse;
8687
import com.cloud.api.response.ListResponse;
@@ -487,8 +488,16 @@ private String queueCommand(BaseCmd cmdObj, Map<String, String> params) {
487488
objectEntityTable = createCmd.getEntityTable();
488489
params.put("id", objectId.toString());
489490
} else {
490-
ApiDispatcher.setupParameters(cmdObj, params);
491+
List<ControlledEntity> entitiesToAccess = new ArrayList<ControlledEntity>();
492+
ApiDispatcher.setupParameters(cmdObj, params, entitiesToAccess);
491493
ApiDispatcher.plugService(cmdObj);
494+
495+
if(!entitiesToAccess.isEmpty()){
496+
Account owner = s_instance._accountMgr.getActiveAccountById(cmdObj.getEntityOwnerId());
497+
s_instance._accountMgr.checkAccess(caller, null, true, owner);
498+
499+
s_instance._accountMgr.checkAccess(caller, null, true, (ControlledEntity[])entitiesToAccess.toArray());
500+
}
492501
}
493502

494503
BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj;

0 commit comments

Comments
 (0)