Skip to content

Commit

Permalink
Migrate cert decision recording to new autz
Browse files Browse the repository at this point in the history
Instead of hardcoded authorization logic for certification decision
recording that used #all and #recordCertificationDecision (for own
decisions), we now use general parameterized #completeWorkItem action.
The legacy #recordCertificationDecision is automatically converted
to the new action on the fly (in memory).

To do that, this commit provides preliminary implementation of
sub-object authorization parameters.
  • Loading branch information
mederly committed Jun 8, 2023
1 parent 92c1860 commit f7ff2db
Show file tree
Hide file tree
Showing 23 changed files with 523 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,10 @@ public static boolean isObjectable(@NotNull Class<?> type) {
return Objectable.class.isAssignableFrom(type);
}

public static <O extends ObjectType> PrismObjectValue<O> getValue(PrismObject<O> object) {
return object != null ? object.getValue() : null;
}

@FunctionalInterface
private interface ExtensionItemRemover {
// Removes item (known from the context) from the extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,18 @@ public void clearTemporaryPrincipalOid() {
}

@Override
public <O extends ObjectType, T extends ObjectType> void failAuthorization(String operationUrl,
AuthorizationPhaseType phase, AuthorizationParameters<O, T> params,
public void failAuthorization(String operationUrl,
AuthorizationPhaseType phase, AbstractAuthorizationParameters params,
OperationResult result) throws SecurityViolationException {
securityEnforcer.failAuthorization(operationUrl, phase, params, result);
}

// MidPoint pages invoke this method (through PageBase)
@Override
public <O extends ObjectType, T extends ObjectType> boolean isAuthorized(
public boolean isAuthorized(
@NotNull String operationUrl,
@Nullable AuthorizationPhaseType phase,
@NotNull AuthorizationParameters<O, T> params,
@NotNull AbstractAuthorizationParameters params,
@Nullable OwnerResolver ownerResolver,
@NotNull Task task,
@NotNull OperationResult result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
/**
* @author skublik
*/

public class MidpointAllowAllAuthorizationEvaluator extends MidPointGuiAuthorizationEvaluator {

public MidpointAllowAllAuthorizationEvaluator(SecurityEnforcer securityEnforcer, SecurityContextManager securityContextManager,
TaskManager taskManager) {
public MidpointAllowAllAuthorizationEvaluator(
SecurityEnforcer securityEnforcer, SecurityContextManager securityContextManager, TaskManager taskManager) {
super(securityEnforcer, securityContextManager, taskManager);
}

@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.certification.api;

import org.jetbrains.annotations.NotNull;

import java.io.Serializable;

public record AccessCertificationCaseId(@NotNull String campaignOid, long caseId) implements Serializable {

@Override
public String toString() {
return campaignOid + ":" + caseId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.certification.api;

import java.io.Serializable;

import org.jetbrains.annotations.NotNull;

public record AccessCertificationWorkItemId(@NotNull AccessCertificationCaseId caseId, long workItemId) implements Serializable {

public static AccessCertificationWorkItemId of(@NotNull String campaignOid, long caseId, long workItemId) {
return new AccessCertificationWorkItemId(
new AccessCertificationCaseId(campaignOid, caseId),
workItemId);
}

@Override
public String toString() {
return caseId + ":" + workItemId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@

package com.evolveum.midpoint.certification.api;

import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommunicationException;
Expand All @@ -22,9 +19,6 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;

/**
* BEWARE: CertificationManager is responsible for authorizing all actions carried out through it.
*/
Expand Down Expand Up @@ -99,17 +93,22 @@ AccessCertificationCampaignType createCampaign(String definitionOid, Task task,

/**
* Records a particular decision of a reviewer.
* @param campaignOid OID of the campaign to which the decision belongs.
* @param caseId ID of the certification case to which the decision belongs.
* @param workItemId ID of the work item to which the decision belongs.
*
* @param workItemId Complex ID of the work item to which the decision belongs.
* @param response The response.
* @param comment Reviewer's comment.
* @param preAuthorized Is the request already authorized?
* @param task Task in context of which all operations will take place.
* @param parentResult Result for the operations.
* @param result Result for the operations.
*/
void recordDecision(String campaignOid, long caseId, long workItemId, AccessCertificationResponseType response,
void recordDecision(
@NotNull AccessCertificationWorkItemId workItemId,
AccessCertificationResponseType response,
String comment,
Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, ConfigurationException;
boolean preAuthorized,
Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException,
ExpressionEvaluationException, CommunicationException, ConfigurationException;

/**
* Provides statistical information about outcomes of cases in a given campaign.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,40 @@

package com.evolveum.midpoint.certification.impl;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

import static com.evolveum.midpoint.certification.api.OutcomeUtils.normalizeToNull;
import static com.evolveum.midpoint.certification.api.OutcomeUtils.toUri;
import static com.evolveum.midpoint.schema.util.CertCampaignTypeUtil.norm;
import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.toShortString;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemType.F_ASSIGNEE_REF;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemType.F_ESCALATION_LEVEL;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType.F_CASE;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType.F_STAGE;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType.*;

import java.util.*;
import javax.xml.datatype.XMLGregorianCalendar;

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.evolveum.midpoint.certification.api.AccessCertificationWorkItemId;
import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismValueCollectionsUtil;
import com.evolveum.midpoint.prism.delta.*;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ItemDeltaCollectionsUtil;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.expression.VariablesMap;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.expression.VariablesMap;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.*;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.cases.ApprovalContextUtil;
import com.evolveum.midpoint.schema.util.cases.CaseRelatedUtils;
import com.evolveum.midpoint.schema.util.cases.WorkItemTypeUtil;
Expand All @@ -31,22 +54,6 @@
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.xml.datatype.XMLGregorianCalendar;
import java.util.*;

import static com.evolveum.midpoint.certification.api.OutcomeUtils.*;
import static com.evolveum.midpoint.schema.util.CertCampaignTypeUtil.norm;
import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.toShortString;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemType.F_ASSIGNEE_REF;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemType.F_ESCALATION_LEVEL;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType.F_CASE;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType.F_STAGE;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType.*;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

/**
* Logic for certification operations like decision recording, case creation or advancement.
Expand All @@ -68,29 +75,24 @@ public class AccCertCaseOperationsHelper {
/**
* Records a decision. Updates necessary items like the outcomes.
*/
void recordDecision(String campaignOid, long caseId, long workItemId, AccessCertificationResponseType response,
String comment, Task task, OperationResult result) throws SecurityViolationException, ObjectNotFoundException,
void recordDecision(
AccessCertificationWorkItemId complexWorkItemId,
WorkItemInContext workItemInContext,
AccessCertificationResponseType response,
String comment,
Task task,
OperationResult result) throws SecurityViolationException, ObjectNotFoundException,
SchemaException, ObjectAlreadyExistsException {
AccessCertificationCaseType acase = queryHelper.getCase(campaignOid, caseId, task, result);
if (acase == null) {
throw new ObjectNotFoundException(
"Case " + caseId + " was not found in campaign " + campaignOid,
AccessCertificationCaseType.class,
campaignOid + ":" + caseId);
}
AccessCertificationCampaignType campaign = CertCampaignTypeUtil.getCampaign(acase);
if (campaign == null) {
throw new IllegalStateException("No owning campaign present in case " + acase);
}
AccessCertificationWorkItemType workItem = CertCampaignTypeUtil.findWorkItem(acase, workItemId);
if (workItem == null) {
throw new ObjectNotFoundException(
"Work item " + workItemId + " was not found in campaign " + toShortString(campaign) + ", case " + caseId,
AccessCertificationWorkItemType.class,
campaignOid + ":" + workItemId);
}

ObjectReferenceType responderRef = ObjectTypeUtil.createObjectRef(securityContextManager.getPrincipal().getFocus(), prismContext);
var complexCaseId = complexWorkItemId.caseId();
long workItemId = complexWorkItemId.workItemId();
long caseId = complexCaseId.caseId();
String campaignOid = complexCaseId.campaignOid();

AccessCertificationCampaignType campaign = workItemInContext.campaign();
AccessCertificationCaseType aCase = workItemInContext.aCase();

ObjectReferenceType responderRef = ObjectTypeUtil.createObjectRef(securityContextManager.getPrincipal().getFocus());
XMLGregorianCalendar now = clock.currentTimeXMLGregorianCalendar();
ItemPath workItemPath = ItemPath.create(F_CASE, caseId, F_WORK_ITEM, workItemId);
Collection<ItemDelta<?,?>> deltaList = prismContext.deltaFor(AccessCertificationCampaignType.class)
Expand All @@ -103,8 +105,10 @@ void recordDecision(String campaignOid, long caseId, long workItemId, AccessCert
.asItemDeltas();
ItemDeltaCollectionsUtil.applyTo(deltaList, campaign.asPrismContainerValue()); // to have data for outcome computation

AccessCertificationResponseType newCurrentOutcome = computationHelper.computeOutcomeForStage(acase, campaign, campaign.getStageNumber());
AccessCertificationResponseType newOverallOutcome = computationHelper.computeOverallOutcome(acase, campaign, campaign.getStageNumber(), newCurrentOutcome);
AccessCertificationResponseType newCurrentOutcome =
computationHelper.computeOutcomeForStage(aCase, campaign, campaign.getStageNumber());
AccessCertificationResponseType newOverallOutcome =
computationHelper.computeOverallOutcome(aCase, campaign, campaign.getStageNumber(), newCurrentOutcome);
deltaList.addAll(prismContext.deltaFor(AccessCertificationCampaignType.class)
.item(F_CASE, caseId, F_CURRENT_STAGE_OUTCOME).replace(toUri(newCurrentOutcome))
.item(F_CASE, caseId, F_OUTCOME).replace(toUri(newOverallOutcome))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@

package com.evolveum.midpoint.certification.impl;

import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.toShortString;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType.F_WORK_ITEM;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationWorkItemType.*;

import java.util.ArrayList;
import java.util.List;

import com.evolveum.midpoint.certification.api.AccessCertificationCaseId;

import com.evolveum.midpoint.certification.api.AccessCertificationWorkItemId;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.VisibleForTesting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
Expand All @@ -26,7 +34,6 @@
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
Expand Down Expand Up @@ -95,12 +102,12 @@ List<AccessCertificationCaseType> getOpenCasesForReviewer(
return searchCases(campaign.getOid(), prismContext.queryFactory().createQuery(filter), result);
}

public AccessCertificationCaseType getCase(String campaignOid, long caseId, @SuppressWarnings("unused") Task task,
OperationResult result) throws SchemaException {
public AccessCertificationCaseType getCase(
@NotNull AccessCertificationCaseId caseId, OperationResult result) throws SchemaException {
QueryFactory queryFactory = prismContext.queryFactory();
ObjectFilter filter = queryFactory.createAnd(
queryFactory.createOwnerHasOidIn(campaignOid),
queryFactory.createInOid(String.valueOf(caseId))
queryFactory.createOwnerHasOidIn(caseId.campaignOid()),
queryFactory.createInOid(String.valueOf(caseId.caseId()))
);
ObjectQuery query = queryFactory.createQuery(filter);

Expand All @@ -111,8 +118,33 @@ public AccessCertificationCaseType getCase(String campaignOid, long caseId, @Sup
} else if (caseList.size() == 1) {
return caseList.get(0);
} else {
throw new IllegalStateException("More than one certification case with ID " + caseId + " in campaign " + campaignOid);
throw new IllegalStateException("More than one certification case with ID " + caseId);
}
}

@NotNull WorkItemInContext getWorkItemInContext(AccessCertificationWorkItemId workItemId, OperationResult result)
throws SchemaException, ObjectNotFoundException {
AccessCertificationCaseId caseId = workItemId.caseId();
AccessCertificationCaseType aCase = getCase(caseId, result);
if (aCase == null) {
throw new ObjectNotFoundException(
"Case " + caseId + " was not found",
AccessCertificationCaseType.class,
caseId.toString());
}
AccessCertificationCampaignType campaign = CertCampaignTypeUtil.getCampaign(aCase);
if (campaign == null) {
throw new IllegalStateException("No owning campaign present in case " + aCase);
}
AccessCertificationWorkItemType workItem = CertCampaignTypeUtil.findWorkItem(aCase, workItemId.workItemId());
if (workItem == null) {
throw new ObjectNotFoundException(
"Work item %d was not found in campaign %s, case %s"
.formatted(workItemId.workItemId(), toShortString(campaign), caseId),
AccessCertificationWorkItemType.class,
workItemId.toString());
}
return new WorkItemInContext(campaign, aCase, workItem);
}

List<AccessCertificationCaseType> selectOpenCasesForReviewer(List<AccessCertificationCaseType> caseList, String reviewerOid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.evolveum.midpoint.certification.impl;

import com.evolveum.midpoint.certification.api.AccessCertificationWorkItemId;
import com.evolveum.midpoint.certification.api.OutcomeUtils;
import com.evolveum.midpoint.model.impl.trigger.SingleTriggerHandler;
import com.evolveum.midpoint.model.api.trigger.TriggerHandlerRegistry;
Expand Down Expand Up @@ -124,10 +125,12 @@ private void executeCompleteAction(AccessCertificationCampaignType campaign, Com
for (AccessCertificationWorkItemType workItem : workItems) {
AccessCertificationCaseType aCase = CertCampaignTypeUtil.getCase(workItem);
if (aCase == null || aCase.getId() == null || workItem.getId() == null) {
LOGGER.error("Couldn't auto-complete work item {} in case {}: some identifiers are missing", aCase, workItem); // shouldn't occur
// shouldn't occur
LOGGER.error("Couldn't auto-complete work item {} in case {}: some identifiers are missing", aCase, workItem);
} else {
certManager.recordDecision(campaign.getOid(), aCase.getId(), workItem.getId(),
OutcomeUtils.fromUri(completeAction.getOutcome()), null, task, result);
certManager.recordDecision(
AccessCertificationWorkItemId.of(campaign.getOid(), aCase.getId(), workItem.getId()),
OutcomeUtils.fromUri(completeAction.getOutcome()), null, true, task, result);
}
}
}
Expand Down

0 comments on commit f7ff2db

Please sign in to comment.