SecurityChecker can accept multiple ControlledEntity

This commit is contained in:
Prachi Damle 2014-04-01 11:25:23 -07:00 committed by Min Chen
parent ecbaa11f09
commit 897e0d3abe
5 changed files with 186 additions and 23 deletions

View File

@ -100,6 +100,26 @@ public interface SecurityChecker extends Adapter {
*/
boolean checkAccess(Account caller, ControlledEntity entity, AccessType accessType, String action) throws PermissionDeniedException;
/**
* Checks if the account can access multiple objects.
*
* @param caller
* account to check against.
* @param entities
* objects that the account is trying to access.
* @param accessType
* TODO
* @param action
* name of the API
* @return true if access allowed. false if this adapter cannot provide
* permission.
* @throws PermissionDeniedException
* if this adapter is suppose to authenticate ownership and the
* check failed.
*/
boolean checkAccess(Account caller, AccessType accessType, String action, ControlledEntity... entities)
throws PermissionDeniedException;
/**
* Checks if the user belongs to an account that can access the object.

View File

@ -341,4 +341,17 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
}
return checkAccess(caller, entity, accessType);
}
@Override
public boolean checkAccess(Account caller, AccessType accessType, String action, ControlledEntity... entities)
throws PermissionDeniedException {
// returns true only if access to all entities is granted
for (ControlledEntity entity : entities) {
if (!checkAccess(caller, entity, accessType, action)) {
return false;
}
}
return true;
}
}

View File

@ -37,8 +37,10 @@ import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.InfrastructureEntity;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.BaseCmd;
@ -54,7 +56,12 @@ import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.context.CallContext;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.DataCenter;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.DateUtil;
@ -71,6 +78,17 @@ public class ParamProcessWorker implements DispatchWorker {
@Inject
protected EntityManager _entityMgr;
List<SecurityChecker> _secChecker;
public List<SecurityChecker> getSecChecker() {
return _secChecker;
}
@Inject
public void setSecChecker(List<SecurityChecker> secChecker) {
_secChecker = secChecker;
}
@Override
public void handle(final DispatchTask task) {
processParameters(task.getCmd(), task.getParams());
@ -214,27 +232,96 @@ public class ParamProcessWorker implements DispatchWorker {
private void doAccessChecks(final BaseCmd cmd, final Map<Object, AccessType> entitiesToAccess) {
final Account caller = CallContext.current().getCallingAccount();
final Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
Account caller = CallContext.current().getCallingAccount();
if (cmd instanceof BaseAsyncCreateCmd) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
}
APICommand commandAnnotation = cmd.getClass().getAnnotation(APICommand.class);
String apiName = commandAnnotation != null ? commandAnnotation.name() : null;
if (!entitiesToAccess.isEmpty()) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
for (final Object entity : entitiesToAccess.keySet()) {
List<ControlledEntity> entitiesToOperate = new ArrayList<ControlledEntity>();
for (Object entity : entitiesToAccess.keySet()) {
if (entity instanceof ControlledEntity) {
_accountMgr.checkAccess(caller, entitiesToAccess.get(entity), true, (ControlledEntity)entity);
if (AccessType.OperateEntry == entitiesToAccess.get(entity)) {
entitiesToOperate.add((ControlledEntity) entity);
} else {
_accountMgr.checkAccess(caller, entitiesToAccess.get(entity), false, apiName,
(ControlledEntity) entity);
}
} else if (entity instanceof InfrastructureEntity) {
//FIXME: Move this code in adapter, remove code from Account manager
if (entity instanceof DataCenter) {
checkZoneAccess(caller, (DataCenter) entity);
} else if (entity instanceof ServiceOffering) {
checkServiceOfferingAccess(caller, (ServiceOffering) entity);
} else if (entity instanceof DiskOffering) {
checkDiskOfferingAccess(caller, (DiskOffering) entity);
}
}
}
if (!entitiesToOperate.isEmpty()) {
_accountMgr.checkAccess(caller, AccessType.OperateEntry, false, apiName,
(ControlledEntity[]) entitiesToOperate.toArray());
}
}
}
private void checkDiskOfferingAccess(Account caller, DiskOffering dof) {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, dof)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to disk offering:" + dof.getId() + " by "
+ checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to disk offering:"
+ dof.getId());
}
private void checkServiceOfferingAccess(Account caller, ServiceOffering sof) {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, sof)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to service offering:" + sof.getId() + " by "
+ checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to service offering:"
+ sof.getId());
}
private void checkZoneAccess(Account caller, DataCenter zone) {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, zone)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to zone:" + zone.getId() + " by "
+ checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName()
+ " for zone " + zone.getId());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to zone:"
+ zone.getId());
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object paramObj, final Parameter annotation) throws IllegalArgumentException, ParseException {

View File

@ -216,7 +216,6 @@ public class RoleBasedAPIAccessChecker extends AdapterBase implements APIChecker
}
private void addDefaultAclPolicyPermission(String apiName, Class<?> cmdClass, RoleType role) {
AccessType accessType = null;
Class<?>[] entityTypes = null;
if (cmdClass != null) {

View File

@ -27,6 +27,7 @@ import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.PermissionScope;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.iam.api.IAMGroup;
import org.apache.cloudstack.iam.api.IAMPolicy;
@ -107,14 +108,22 @@ public class RoleBasedEntityAccessChecker extends DomainChecker implements Secur
permissions = _iamSrv.listPolicyPermissionByActionAndEntity(policy.getId(), action, entityType);
if (permissions.isEmpty()) {
if (accessType != null) {
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
accessType.toString(), entityType));
for (AccessType type : AccessType.values()) {
if (type.ordinal() >= accessType.ordinal()) {
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
type.toString(), entityType));
}
}
}
}
} else {
if (accessType != null) {
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
accessType.toString(), entityType));
for (AccessType type : AccessType.values()) {
if (type.ordinal() >= accessType.ordinal()) {
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
type.toString(), entityType));
}
}
}
}
for (IAMPolicyPermission permission : permissions) {
@ -145,6 +154,48 @@ public class RoleBasedEntityAccessChecker extends DomainChecker implements Secur
return false;
}
@Override
public boolean checkAccess(Account caller, AccessType accessType, String action, ControlledEntity... entities)
throws PermissionDeniedException {
// operate access on multiple entities?
if (accessType != null && accessType == AccessType.OperateEntry) {
// In this case caller MUST own n-1 entities.
for (ControlledEntity entity : entities) {
checkAccess(caller, entity, accessType, action);
boolean otherEntitiesAccess = true;
for (ControlledEntity otherEntity : entities) {
if (otherEntity.getAccountId() == caller.getAccountId()
|| (checkAccess(caller, otherEntity, accessType, action) && otherEntity.getAccountId() == entity
.getAccountId())) {
continue;
} else {
otherEntitiesAccess = false;
break;
}
}
if (otherEntitiesAccess) {
return true;
}
}
throw new PermissionDeniedException(caller
+ " does not have permission to perform this operation on these resources");
} else {
for (ControlledEntity entity : entities) {
if (!checkAccess(caller, entity, accessType, action)) {
return false;
}
}
return true;
}
}
private boolean checkPermissionScope(Account caller, String scope, Long scopeId, ControlledEntity entity) {
if(scopeId != null && !scopeId.equals(new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER))){
@ -181,15 +232,8 @@ public class RoleBasedEntityAccessChecker extends DomainChecker implements Secur
private List<IAMPolicy> getEffectivePolicies(Account caller, ControlledEntity entity) {
// Get the static Policies of the Caller
List<IAMPolicy> policies = _iamSrv.listIAMPolicies(caller.getId());
// add any dynamic policies w.r.t the entity
if (caller.getId() == entity.getAccountId()) {
// The caller owns the entity
policies.add(_iamSrv.getResourceOwnerPolicy());
}
List<IAMGroup> groups = _iamSrv.listIAMGroups(caller.getId());
for (IAMGroup group : groups) {
// for each group find the grand parent groups.