mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 20:02:29 +01:00
SecurityChecker can accept multiple ControlledEntity
This commit is contained in:
parent
ecbaa11f09
commit
897e0d3abe
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
} else if (entity instanceof InfrastructureEntity) {
|
||||
//FIXME: Move this code in adapter, remove code from Account manager
|
||||
|
||||
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) {
|
||||
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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
for (AccessType type : AccessType.values()) {
|
||||
if (type.ordinal() >= accessType.ordinal()) {
|
||||
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
||||
accessType.toString(), entityType));
|
||||
type.toString(), entityType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (accessType != null) {
|
||||
for (AccessType type : AccessType.values()) {
|
||||
if (type.ordinal() >= accessType.ordinal()) {
|
||||
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
||||
accessType.toString(), entityType));
|
||||
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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user