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;
|
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.
|
* 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);
|
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.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.InfrastructureEntity;
|
import org.apache.cloudstack.acl.InfrastructureEntity;
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
import org.apache.cloudstack.api.ACL;
|
import org.apache.cloudstack.api.ACL;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiErrorCode;
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||||
import org.apache.cloudstack.api.BaseCmd;
|
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.api.command.user.event.ListEventsCmd;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
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.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.Account;
|
||||||
import com.cloud.user.AccountManager;
|
import com.cloud.user.AccountManager;
|
||||||
import com.cloud.utils.DateUtil;
|
import com.cloud.utils.DateUtil;
|
||||||
@ -71,6 +78,17 @@ public class ParamProcessWorker implements DispatchWorker {
|
|||||||
@Inject
|
@Inject
|
||||||
protected EntityManager _entityMgr;
|
protected EntityManager _entityMgr;
|
||||||
|
|
||||||
|
List<SecurityChecker> _secChecker;
|
||||||
|
|
||||||
|
public List<SecurityChecker> getSecChecker() {
|
||||||
|
return _secChecker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public void setSecChecker(List<SecurityChecker> secChecker) {
|
||||||
|
_secChecker = secChecker;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(final DispatchTask task) {
|
public void handle(final DispatchTask task) {
|
||||||
processParameters(task.getCmd(), task.getParams());
|
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) {
|
private void doAccessChecks(final BaseCmd cmd, final Map<Object, AccessType> entitiesToAccess) {
|
||||||
final Account caller = CallContext.current().getCallingAccount();
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
final Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
|
|
||||||
|
|
||||||
if (cmd instanceof BaseAsyncCreateCmd) {
|
APICommand commandAnnotation = cmd.getClass().getAnnotation(APICommand.class);
|
||||||
//check that caller can access the owner account.
|
String apiName = commandAnnotation != null ? commandAnnotation.name() : null;
|
||||||
_accountMgr.checkAccess(caller, null, true, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entitiesToAccess.isEmpty()) {
|
if (!entitiesToAccess.isEmpty()) {
|
||||||
//check that caller can access the owner account.
|
List<ControlledEntity> entitiesToOperate = new ArrayList<ControlledEntity>();
|
||||||
_accountMgr.checkAccess(caller, null, true, owner);
|
|
||||||
for (final Object entity : entitiesToAccess.keySet()) {
|
for (Object entity : entitiesToAccess.keySet()) {
|
||||||
if (entity instanceof ControlledEntity) {
|
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) {
|
} 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"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object paramObj, final Parameter annotation) throws IllegalArgumentException, ParseException {
|
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) {
|
private void addDefaultAclPolicyPermission(String apiName, Class<?> cmdClass, RoleType role) {
|
||||||
|
|
||||||
AccessType accessType = null;
|
AccessType accessType = null;
|
||||||
Class<?>[] entityTypes = null;
|
Class<?>[] entityTypes = null;
|
||||||
if (cmdClass != null) {
|
if (cmdClass != null) {
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import org.apache.log4j.Logger;
|
|||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.PermissionScope;
|
import org.apache.cloudstack.acl.PermissionScope;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker;
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
import org.apache.cloudstack.api.InternalIdentity;
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
import org.apache.cloudstack.iam.api.IAMGroup;
|
import org.apache.cloudstack.iam.api.IAMGroup;
|
||||||
import org.apache.cloudstack.iam.api.IAMPolicy;
|
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);
|
permissions = _iamSrv.listPolicyPermissionByActionAndEntity(policy.getId(), action, entityType);
|
||||||
if (permissions.isEmpty()) {
|
if (permissions.isEmpty()) {
|
||||||
if (accessType != null) {
|
if (accessType != null) {
|
||||||
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
for (AccessType type : AccessType.values()) {
|
||||||
accessType.toString(), entityType));
|
if (type.ordinal() >= accessType.ordinal()) {
|
||||||
|
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
||||||
|
type.toString(), entityType));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (accessType != null) {
|
if (accessType != null) {
|
||||||
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
for (AccessType type : AccessType.values()) {
|
||||||
accessType.toString(), entityType));
|
if (type.ordinal() >= accessType.ordinal()) {
|
||||||
|
permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(),
|
||||||
|
type.toString(), entityType));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (IAMPolicyPermission permission : permissions) {
|
for (IAMPolicyPermission permission : permissions) {
|
||||||
@ -145,6 +154,48 @@ public class RoleBasedEntityAccessChecker extends DomainChecker implements Secur
|
|||||||
return false;
|
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) {
|
private boolean checkPermissionScope(Account caller, String scope, Long scopeId, ControlledEntity entity) {
|
||||||
|
|
||||||
if(scopeId != null && !scopeId.equals(new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER))){
|
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) {
|
private List<IAMPolicy> getEffectivePolicies(Account caller, ControlledEntity entity) {
|
||||||
|
|
||||||
// Get the static Policies of the Caller
|
|
||||||
List<IAMPolicy> policies = _iamSrv.listIAMPolicies(caller.getId());
|
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());
|
List<IAMGroup> groups = _iamSrv.listIAMGroups(caller.getId());
|
||||||
for (IAMGroup group : groups) {
|
for (IAMGroup group : groups) {
|
||||||
// for each group find the grand parent groups.
|
// for each group find the grand parent groups.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user