use of com.evolveum.midpoint.schema.AccessDecision in project midpoint by Evolveum.
the class ClockworkAuthorizationHelper method authorizeAssignmentRequest.
private <F extends ObjectType, O extends ObjectType> void authorizeAssignmentRequest(LensContext<F> context, String operationUrl, String assignActionUrl, ItemName assignmentElementQName, PrismObject<O> object, OwnerResolver ownerResolver, ObjectSecurityConstraints securityConstraints, PlusMinusZero plusMinusZero, boolean prohibitPolicies, Task task, OperationResult result) throws SecurityViolationException, SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException {
// This is *request* authorization. Therefore we care only about primary delta.
ObjectDelta<F> focusPrimaryDelta = context.getFocusContext().getPrimaryDelta();
if (focusPrimaryDelta == null) {
return;
}
ContainerDelta<AssignmentType> focusAssignmentDelta = focusPrimaryDelta.findContainerDelta(assignmentElementQName);
if (focusAssignmentDelta == null) {
return;
}
String operationDesc = assignActionUrl.substring(assignActionUrl.lastIndexOf('#') + 1);
Collection<PrismContainerValue<AssignmentType>> changedAssignmentValues = determineChangedAssignmentValues(context.getFocusContext(), assignmentElementQName, focusAssignmentDelta, plusMinusZero);
for (PrismContainerValue<AssignmentType> changedAssignmentValue : changedAssignmentValues) {
AssignmentType changedAssignment = changedAssignmentValue.getRealValue();
ObjectReferenceType targetRef = changedAssignment.getTargetRef();
if (targetRef == null || targetRef.getOid() == null) {
// This may still be allowed by #add and #modify authorizations. We have already checked these, but there may be combinations of
// assignments, one of the assignments allowed by #assign, other allowed by #modify (e.g. MID-4517).
// Therefore check the items again. This is not very efficient to check it twice. But this is not a common case
// so there should not be any big harm in suffering this inefficiency.
AccessDecision subitemDecision = securityEnforcer.determineSubitemDecision(securityConstraints, changedAssignmentValue, operationUrl, getRequestAuthorizationPhase(context), null, plusMinusZero, operationDesc);
if (subitemDecision == AccessDecision.ALLOW) {
LOGGER.debug("{} of policy {} to {} allowed with {} authorization", operationDesc, assignmentElementQName.getLocalPart(), object, operationUrl);
continue;
} else {
LOGGER.debug("{} of non-target {} not allowed", operationDesc, assignmentElementQName.getLocalPart());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Denied request for object {}: {} of non-target {} not allowed", object, operationDesc, assignmentElementQName.getLocalPart());
}
securityEnforcer.failAuthorization(operationDesc, getRequestAuthorizationPhase(context), AuthorizationParameters.Builder.buildObject(object), result);
}
}
PrismObject<ObjectType> target;
try {
// We do not worry about performance here too much. The target was already evaluated. This will be retrieved from repo cache anyway.
target = objectResolver.resolve(targetRef.asReferenceValue(), "resolving " + assignmentElementQName.getLocalPart() + " target", task, result);
} catch (ObjectNotFoundException e) {
LOGGER.warn("Object {} referenced as {} target in {} was not found", targetRef.asReferenceValue().getOid(), assignmentElementQName.getLocalPart(), object);
target = null;
}
ObjectDelta<O> assignmentObjectDelta = object.createModifyDelta();
ContainerDelta<AssignmentType> assignmentDelta = assignmentObjectDelta.createContainerModification(assignmentElementQName);
// We do not care if this is add or delete. All that matters for authorization is that it is in a delta.
assignmentDelta.addValuesToAdd(changedAssignment.asPrismContainerValue().clone());
QName relation = targetRef.getRelation();
if (relation == null) {
relation = prismContext.getDefaultRelation();
}
List<OrderConstraintsType> orderConstraints = determineOrderConstraints(assignmentElementQName, changedAssignment);
AuthorizationParameters<O, ObjectType> autzParams = new AuthorizationParameters.Builder<O, ObjectType>().oldObject(object).delta(assignmentObjectDelta).target(target).relation(relation).orderConstraints(orderConstraints).build();
if (prohibitPolicies) {
if (changedAssignment.getPolicyRule() != null || !changedAssignment.getPolicyException().isEmpty() || !changedAssignment.getPolicySituation().isEmpty() || !changedAssignment.getTriggeredPolicyRule().isEmpty()) {
// This may still be allowed by #add and #modify authorizations. We have already checked these, but there may be combinations of
// assignments, one of the assignments allowed by #assign, other allowed by #modify (e.g. MID-4517).
// Therefore check the items again. This is not very efficient to check it twice. But this is not a common case
// so there should not be any big harm in suffering this inefficiency.
AccessDecision subitemDecision = securityEnforcer.determineSubitemDecision(securityConstraints, changedAssignmentValue, operationUrl, getRequestAuthorizationPhase(context), null, plusMinusZero, operationDesc);
if (subitemDecision == AccessDecision.ALLOW) {
LOGGER.debug("{} of policy assignment to {} allowed with {} authorization", operationDesc, object, operationUrl);
continue;
} else {
securityEnforcer.failAuthorization("with assignment because of policies in the assignment", getRequestAuthorizationPhase(context), autzParams, result);
}
}
}
if (securityEnforcer.isAuthorized(assignActionUrl, getRequestAuthorizationPhase(context), autzParams, ownerResolver, task, result)) {
LOGGER.debug("{} of target {} to {} allowed with {} authorization", operationDesc, target, object, assignActionUrl);
continue;
}
if (relationRegistry.isDelegation(relation)) {
if (securityEnforcer.isAuthorized(ModelAuthorizationAction.DELEGATE.getUrl(), getRequestAuthorizationPhase(context), autzParams, ownerResolver, task, result)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} of target {} to {} allowed with {} authorization", operationDesc, target, object, ModelAuthorizationAction.DELEGATE.getUrl());
}
continue;
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} of target {} to {} denied", operationDesc, target, object);
}
securityEnforcer.failAuthorization("with " + assignmentElementQName.getLocalPart(), getRequestAuthorizationPhase(context), autzParams, result);
}
}
use of com.evolveum.midpoint.schema.AccessDecision in project midpoint by Evolveum.
the class SandboxTypeCheckingExtension method onMethodSelection.
@Override
public void onMethodSelection(final Expression expression, final MethodNode target) {
ClassNode targetDeclaringClass = target.getDeclaringClass();
AccessDecision decision = decideClass(targetDeclaringClass.getName(), target.getName());
if (decision != AccessDecision.ALLOW) {
StringBuilder sb = new StringBuilder(GroovyScriptEvaluator.SANDBOX_ERROR_PREFIX);
sb.append("Access to Groovy method ");
sb.append(targetDeclaringClass.getName()).append("#").append(target.getName()).append(" ");
if (decision == AccessDecision.DENY) {
sb.append("denied");
} else {
sb.append("not allowed");
}
if (getContext().getExpressionProfile() != null) {
sb.append(" (applied expression profile '").append(getContext().getExpressionProfile().getIdentifier()).append("')");
}
addStaticTypeError(sb.toString(), expression);
}
}
use of com.evolveum.midpoint.schema.AccessDecision in project midpoint by Evolveum.
the class SecurityEnforcerImpl method isAuthorizedPhase.
private <O extends ObjectType, T extends ObjectType> AccessDecision isAuthorizedPhase(MidPointPrincipal midPointPrincipal, String operationUrl, AuthorizationPhaseType phase, AuthorizationParameters<O, T> params, OwnerResolver ownerResolver, Consumer<Authorization> applicableAutzConsumer, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
if (AuthorizationConstants.AUTZ_NO_ACCESS_URL.equals(operationUrl)) {
return AccessDecision.DENY;
}
if (phase == null) {
throw new IllegalArgumentException("No phase");
}
AccessDecision decision = AccessDecision.DEFAULT;
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("AUTZ: evaluating authorization principal={}, op={}, phase={}, {}", getUsername(midPointPrincipal), operationUrl, phase, params.shortDump());
}
final AutzItemPaths allowedItems = new AutzItemPaths();
Collection<Authorization> authorities = getAuthorities(midPointPrincipal);
if (authorities != null) {
for (GrantedAuthority authority : authorities) {
if (authority instanceof Authorization) {
Authorization autz = (Authorization) authority;
String autzHumanReadableDesc = autz.getHumanReadableDesc();
LOGGER.trace(" Evaluating {}", autzHumanReadableDesc);
// action
if (!autz.getAction().contains(operationUrl) && !autz.getAction().contains(AuthorizationConstants.AUTZ_ALL_URL)) {
LOGGER.trace(" {} not applicable for operation {}", autzHumanReadableDesc, operationUrl);
continue;
}
// phase
if (autz.getPhase() == null) {
LOGGER.trace(" {} is applicable for all phases (continuing evaluation)", autzHumanReadableDesc);
} else {
if (autz.getPhase() != phase) {
LOGGER.trace(" {} is not applicable for phases {} (breaking evaluation)", autzHumanReadableDesc, phase);
continue;
} else {
LOGGER.trace(" {} is applicable for phases {} (continuing evaluation)", autzHumanReadableDesc, phase);
}
}
// relation
if (!isApplicableRelation(autz, params.getRelation())) {
LOGGER.trace(" {} not applicable for relation {}", autzHumanReadableDesc, params.getRelation());
continue;
}
// orderConstraints
if (!isApplicableOrderConstraints(autz, params.getOrderConstraints())) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" {} not applicable for orderConstraints {}", autzHumanReadableDesc, SchemaDebugUtil.shortDumpOrderConstraintsList(params.getOrderConstraints()));
}
continue;
}
// object
if (isApplicableObject(autz, params.getOdo(), midPointPrincipal, ownerResolver, autzHumanReadableDesc, task, result)) {
LOGGER.trace(" {} applicable for object {} (continuing evaluation)", autzHumanReadableDesc, params.getAnyObject());
} else {
LOGGER.trace(" {} not applicable for object {}, none of the object specifications match (breaking evaluation)", autzHumanReadableDesc, params.getAnyObject());
continue;
}
// target
if (isApplicable(autz.getTarget(), params.getTarget(), midPointPrincipal, ownerResolver, "target", autzHumanReadableDesc, task, result)) {
LOGGER.trace(" {} applicable for target {} (continuing evaluation)", autzHumanReadableDesc, params.getAnyObject());
} else {
LOGGER.trace(" {} not applicable for target {}, none of the target specifications match (breaking evaluation)", autzHumanReadableDesc, params.getAnyObject());
continue;
}
if (applicableAutzConsumer != null) {
applicableAutzConsumer.accept(autz);
}
// authority is applicable to this situation. now we can process the decision.
AuthorizationDecisionType autzDecision = autz.getDecision();
if (autzDecision == null || autzDecision.equals(AuthorizationDecisionType.ALLOW)) {
allowedItems.collectItems(autz);
LOGGER.trace(" {}: ALLOW operation {} (but continue evaluation)", autzHumanReadableDesc, operationUrl);
decision = AccessDecision.ALLOW;
// Do NOT break here. Other authorization statements may still deny the operation
} else {
// item
if (isApplicableItem(autz, params.getOldObject(), params.getDelta())) {
LOGGER.trace(" {}: Deny authorization applicable for items (continuing evaluation)", autzHumanReadableDesc);
} else {
LOGGER.trace(" {} not applicable for items (breaking evaluation)", autzHumanReadableDesc);
continue;
}
LOGGER.trace(" {}: DENY operation {}", autzHumanReadableDesc, operationUrl);
decision = AccessDecision.DENY;
// Break right here. Deny cannot be overridden by allow. This decision cannot be changed.
break;
}
} else {
LOGGER.warn("Unknown authority type {} in user {}", authority.getClass(), getUsername(midPointPrincipal));
}
}
}
if (decision == AccessDecision.ALLOW) {
// Still check allowedItems. We may still deny the operation.
if (allowedItems.isAllItems()) {
// This means all items are allowed. No need to check anything
LOGGER.trace(" Empty list of allowed items, operation allowed");
} else {
// all items in the object and delta must be allowed
LOGGER.trace(" Checking for allowed items: {}", allowedItems);
ItemDecisionFunction itemDecisionFunction = (itemPath, removingContainer) -> decideAllowedItems(itemPath, allowedItems, phase, removingContainer);
AccessDecision itemsDecision = null;
if (params.hasDelta()) {
// Behave as if this is execution phase for delete delta authorizations. We do not want to avoid deleting objects just because there
// are automatic/operational items that were generated by midPoint. Otherwise we won't be really able to delete any object.
ItemDecisionFunction itemDecisionFunctionDelete = (itemPath, removingContainer) -> decideAllowedItems(itemPath, allowedItems, AuthorizationPhaseType.EXECUTION, removingContainer);
itemsDecision = determineDeltaDecision(params.getDelta(), params.getOldObject(), itemDecisionFunction, itemDecisionFunctionDelete);
} else if (params.hasObject()) {
itemsDecision = determineObjectDecision(params.getAnyObject(), itemDecisionFunction);
}
if (itemsDecision != AccessDecision.ALLOW) {
LOGGER.trace(" NOT ALLOWED operation because the item decision is {}", itemsDecision);
decision = AccessDecision.DEFAULT;
}
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("AUTZ result: principal={}, operation={}: {}", getUsername(midPointPrincipal), prettyActionUrl(operationUrl), decision);
}
return decision;
}
use of com.evolveum.midpoint.schema.AccessDecision in project midpoint by Evolveum.
the class ClockworkAuthorizationHelper method processAssignment.
private <F extends ObjectType, O extends ObjectType> void processAssignment(LensContext<F> context, LensElementContext<O> elementContext, ObjectDelta<O> primaryDeltaClone, String deltaOperationUrl, ItemName assignmentElementQName, PrismObject<O> object, OwnerResolver ownerResolver, ObjectSecurityConstraints securityConstraints, Task task, OperationResult result) throws SecurityViolationException, SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException {
PrismObject<O> currentObject = elementContext.getObjectCurrent();
if (currentObject == null) {
currentObject = elementContext.getObjectOld();
}
if (primaryDeltaClone.hasItemOrSubitemDelta(assignmentElementQName)) {
AccessDecision assignmentItemDecision = determineDecisionForAssignmentItems(securityConstraints, primaryDeltaClone, currentObject, deltaOperationUrl, assignmentElementQName, getRequestAuthorizationPhase(context));
LOGGER.trace("Security decision for {} items: {}", assignmentElementQName.getLocalPart(), assignmentItemDecision);
if (assignmentItemDecision == AccessDecision.ALLOW) {
// Nothing to do, operation is allowed for all values
LOGGER.debug("Allow assignment/unassignment to {} because access to {} container/properties is explicitly allowed", assignmentElementQName.getLocalPart(), object);
} else if (assignmentItemDecision == AccessDecision.DENY) {
LOGGER.debug("Deny assignment/unassignment to {} because access to {} container/properties is explicitly denied", assignmentElementQName.getLocalPart(), object);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Denied request for element context {}: access to {} container/properties is explicitly denied", elementContext.getHumanReadableName(), assignmentElementQName.getLocalPart());
}
throw new AuthorizationException("Access denied");
} else {
AuthorizationDecisionType allItemsDecision = securityConstraints.findAllItemsDecision(deltaOperationUrl, getRequestAuthorizationPhase(context));
if (allItemsDecision == AuthorizationDecisionType.ALLOW) {
// Nothing to do, operation is allowed for all values
} else if (allItemsDecision == AuthorizationDecisionType.DENY) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Denied request for element context {}: access to {} items is explicitly denied", elementContext.getHumanReadableName(), assignmentElementQName.getLocalPart());
}
throw new AuthorizationException("Access denied");
} else {
// No blank decision for assignment modification yet
// process each assignment individually
authorizeAssignmentRequest(context, deltaOperationUrl, ModelAuthorizationAction.ASSIGN.getUrl(), assignmentElementQName, object, ownerResolver, securityConstraints, PlusMinusZero.PLUS, true, task, result);
if (!primaryDeltaClone.isAdd()) {
// We want to allow unassignment even if there are policies. Otherwise we would not be able to get
// rid of that assignment
authorizeAssignmentRequest(context, deltaOperationUrl, ModelAuthorizationAction.UNASSIGN.getUrl(), assignmentElementQName, object, ownerResolver, securityConstraints, PlusMinusZero.MINUS, false, task, result);
}
}
}
// authorization
if (primaryDeltaClone.isAdd()) {
PrismObject<O> objectToAdd = primaryDeltaClone.getObjectToAdd();
objectToAdd.removeContainer(assignmentElementQName);
} else if (primaryDeltaClone.isModify()) {
primaryDeltaClone.removeContainerModification(ItemName.fromQName(assignmentElementQName));
}
}
}
use of com.evolveum.midpoint.schema.AccessDecision in project midpoint by Evolveum.
the class SecurityEnforcerImpl method determineContainerDecision.
private AccessDecision determineContainerDecision(PrismContainerValue<?> cval, ItemDecisionFunction itemDecisionFunction, boolean removingContainer, String decisionContextDesc) {
Collection<Item<?, ?>> items = cval.getItems();
// Note: cval.isEmpty() will also check for id. We do not care about that.
if (items.isEmpty()) {
// TODO: problem with empty containers such as
// orderConstraint in assignment. Skip all
// empty items ... for now.
logSubitemContainerDecision(null, decisionContextDesc, cval);
return null;
}
AccessDecision decision = null;
for (Item<?, ?> item : items) {
ItemPath itemPath = item.getPath();
AccessDecision itemDecision = itemDecisionFunction.decide(itemPath.namedSegmentsOnly(), removingContainer);
logSubitemDecision(itemDecision, decisionContextDesc, itemPath);
if (itemDecision == null) {
// null decision means: skip this
continue;
}
if (itemDecision == AccessDecision.DEFAULT && item instanceof PrismContainer<?>) {
// No decision for entire container. Subitems will dictate the decision.
@SuppressWarnings("unchecked") List<PrismContainerValue<?>> subValues = (List) ((PrismContainer<?>) item).getValues();
AccessDecision containerDecision = null;
for (PrismContainerValue<?> subValue : subValues) {
AccessDecision subdecision = determineContainerDecision(subValue, itemDecisionFunction, removingContainer, decisionContextDesc);
containerDecision = AccessDecision.combine(containerDecision, subdecision);
// We do not want to break the loop immediately here. We want all the denied items to get logged
}
if (containerDecision == null) {
// noinspection UnnecessaryContinue : explicit continue here makes the code more readable
continue;
} else {
decision = AccessDecision.combine(decision, containerDecision);
}
} else {
if (itemDecision == AccessDecision.DENY) {
LOGGER.trace(" DENY operation because item {} in the object is not allowed", itemPath);
// We do not want to break the loop immediately here. We want all the denied items to get logged
}
decision = AccessDecision.combine(decision, itemDecision);
}
}
logSubitemContainerDecision(decision, decisionContextDesc, cval);
return decision;
}
Aggregations