diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java index 8f10504981a..0227dff7dcc 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java @@ -4134,6 +4134,14 @@ public static void dropPowerOfAttorneyIfRequested(OperationResult result, PrismO } } + public static T runUnderPowerOfAttorneyIfNeeded(CheckedProducer producer, PrismObject powerDonor, + PageBase pageBase, Task task, OperationResult result) throws CommonException { + if (powerDonor != null) { + return pageBase.getModelInteractionService().runUnderPowerOfAttorneyChecked(producer, powerDonor, task, result); + } else { + return producer.get(); + } + } @NotNull public static List computeChangesCategorizationList(ChangesByState changesByState, ObjectReferenceType objectRef, diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/CaseWorkItemActionsPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/CaseWorkItemActionsPanel.java index 5e836384733..60c6bb6f809 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/CaseWorkItemActionsPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/CaseWorkItemActionsPanel.java @@ -84,7 +84,7 @@ public void onClick(AjaxRequestTarget ajaxRequestTarget) { } }; - workItemApproveButton.add(new VisibleBehaviour(() -> isApproveRejectButtonVisible())); + workItemApproveButton.add(new VisibleBehaviour(this::isApproveRejectButtonVisible)); workItemApproveButton.setOutputMarkupId(true); actionButtonsContainer.add(workItemApproveButton); @@ -100,7 +100,7 @@ public void onClick(AjaxRequestTarget ajaxRequestTarget) { } }; workItemRejectButton.setOutputMarkupId(true); - workItemRejectButton.add(new VisibleBehaviour(() -> isApproveRejectButtonVisible())); + workItemRejectButton.add(new VisibleBehaviour(this::isApproveRejectButtonVisible)); actionButtonsContainer.add(workItemRejectButton); AjaxButton workItemForwardButton = new AjaxButton(ID_WORK_ITEM_FORWARD_BUTTON, @@ -113,7 +113,7 @@ public void onClick(AjaxRequestTarget ajaxRequestTarget) { } }; workItemForwardButton.setOutputMarkupId(true); - workItemForwardButton.add(new VisibleBehaviour(() -> isForwardButtonVisible())); + workItemForwardButton.add(new VisibleBehaviour(this::isForwardButtonVisible)); actionButtonsContainer.add(workItemForwardButton); AjaxButton workItemClaimButton = new AjaxButton(ID_WORK_ITEM_CLAIM_BUTTON, @@ -240,30 +240,37 @@ public String getObject() { } private boolean isApproveRejectButtonVisible() { - boolean isAuthorized = false; + if (CaseWorkItemUtil.isCaseWorkItemClosed(getModelObject()) || + CaseWorkItemUtil.isWorkItemClaimable(getModelObject())) { + return false; // checking separately to avoid needless authorization checking + } try { OperationResult result = new OperationResult(OPERATION_CHECK_SUBMIT_ACTION_AUTHORIZATION); Task task = getPageBase().createSimpleTask(OPERATION_CHECK_SUBMIT_ACTION_AUTHORIZATION); - isAuthorized = getPageBase().getWorkflowManager().isCurrentUserAuthorizedToSubmit(getModelObject(), task, result); + return WebComponentUtil.runUnderPowerOfAttorneyIfNeeded(() -> + getPageBase().getWorkflowManager().isCurrentUserAuthorizedToSubmit(getModelObject(), task, result), + getPowerDonor(), getPageBase(), task, result); } catch (Exception ex) { LOGGER.error("Cannot check current user authorization to submit work item: {}", ex.getLocalizedMessage(), ex); + return false; } - return CaseWorkItemUtil.isCaseWorkItemNotClosed(getModelObject()) && - !CaseWorkItemUtil.isWorkItemClaimable(getModelObject()) && isAuthorized; - } private boolean isForwardButtonVisible() { - boolean isAuthorized = false; + if (CaseWorkItemUtil.isCaseWorkItemClosed(getModelObject()) || + CaseWorkItemUtil.isWorkItemClaimable(getModelObject())) { + return false; // checking separately to avoid needless authorization checking + } try { OperationResult result = new OperationResult(OPERATION_CHECK_DELEGATE_AUTHORIZATION); Task task = getPageBase().createSimpleTask(OPERATION_CHECK_DELEGATE_AUTHORIZATION); - isAuthorized = getPageBase().getWorkflowManager().isCurrentUserAuthorizedToDelegate(getModelObject(), task, result); + return WebComponentUtil.runUnderPowerOfAttorneyIfNeeded(() -> + getPageBase().getWorkflowManager().isCurrentUserAuthorizedToDelegate(getModelObject(), task, result), + getPowerDonor(), getPageBase(), task, result); } catch (Exception ex) { LOGGER.error("Cannot check current user authorization to submit work item: {}", ex.getLocalizedMessage(), ex); + return false; } - return CaseWorkItemUtil.isCaseWorkItemNotClosed(getModelObject()) && - !CaseWorkItemUtil.isWorkItemClaimable(getModelObject()) && isAuthorized; } private boolean isClaimButtonVisible() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/PageCaseWorkItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/PageCaseWorkItem.java index 54ad9e9c37b..cf92b6d1742 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/PageCaseWorkItem.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/cases/PageCaseWorkItem.java @@ -215,7 +215,12 @@ private void initLayout(){ summaryPanel.setOutputMarkupId(true); add(summaryPanel); - WorkItemDetailsPanel workItemDetailsPanel = new WorkItemDetailsPanel(ID_WORK_ITEM_DETAILS, caseWorkItemModel); + WorkItemDetailsPanel workItemDetailsPanel = new WorkItemDetailsPanel(ID_WORK_ITEM_DETAILS, caseWorkItemModel) { + @Override + protected PrismObject getPowerDonor() { + return PageCaseWorkItem.this.getPowerDonor(); + } + }; workItemDetailsPanel.setOutputMarkupId(true); add(workItemDetailsPanel); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/WorkItemDetailsPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/WorkItemDetailsPanel.java index 53f29106f89..b7f3ca10581 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/WorkItemDetailsPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/WorkItemDetailsPanel.java @@ -19,6 +19,7 @@ import com.evolveum.midpoint.schema.DeltaConvertor; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ApprovalContextUtil; import com.evolveum.midpoint.schema.util.CaseTypeUtil; import com.evolveum.midpoint.schema.util.WorkItemTypeUtil; @@ -34,7 +35,6 @@ import com.evolveum.midpoint.web.component.prism.show.ScenePanel; import com.evolveum.midpoint.web.component.util.VisibleBehaviour; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; -import com.evolveum.midpoint.web.page.admin.cases.ManualCaseTabPanel; import com.evolveum.midpoint.web.page.admin.cases.PageCaseWorkItem; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -55,7 +55,7 @@ /** * Created by honchar */ -public class WorkItemDetailsPanel extends BasePanel{ +public class WorkItemDetailsPanel extends BasePanel { private static final long serialVersionUID = 1L; private static final String DOT_CLASS = WorkItemDetailsPanel.class.getName() + "."; @@ -63,9 +63,7 @@ public class WorkItemDetailsPanel extends BasePanel{ private static final String OPERATION_PREPARE_DELTA_VISUALIZATION = DOT_CLASS + "prepareDeltaVisualization"; private static final String OPERATION_LOAD_CUSTOM_FORM = DOT_CLASS + "loadCustomForm"; private static final String OPERATION_LOAD_CASE_FOCUS_OBJECT = DOT_CLASS + "loadCaseFocusObject"; - private static final String OPERATION_CHECK_SUBMIT_AUTHORIZATION = DOT_CLASS + "checkApproveRejectAuthorization"; - private static final String OPERATION_CHECK_DELEGATE_AUTHORIZATION = DOT_CLASS + "checkDelegateAuthorization"; - private static final String OPERATION_CHECK_CLAIM_AUTHORIZATION = DOT_CLASS + "checkClaimAuthorization"; + private static final String OPERATION_CHECK_ACTIONS_AUTHORIZATION = DOT_CLASS + "checkActionsAuthorization"; private static final String ID_DISPLAY_NAME_PANEL = "displayNamePanel"; private static final String ID_REQUESTED_BY = "requestedBy"; @@ -290,22 +288,25 @@ public String getObject() { } - private boolean isAuthorizedForActions(){ - Task checkApproveRejectAuthTask = getPageBase().createSimpleTask(OPERATION_CHECK_SUBMIT_AUTHORIZATION); - Task checkDelegateAuthTask = getPageBase().createSimpleTask(OPERATION_CHECK_DELEGATE_AUTHORIZATION); + private boolean isAuthorizedForActions() { + Task task = getPageBase().createSimpleTask(OPERATION_CHECK_ACTIONS_AUTHORIZATION); + OperationResult result = task.getResult(); try { - boolean isAuthorizedToSubmit = getPageBase().getWorkflowManager().isCurrentUserAuthorizedToSubmit(getModelObject(), - checkApproveRejectAuthTask, checkApproveRejectAuthTask.getResult()); - boolean isAuthorizedToDelegate = getPageBase().getWorkflowManager().isCurrentUserAuthorizedToDelegate(getModelObject(), - checkDelegateAuthTask, checkDelegateAuthTask.getResult()); - boolean isAuthorizedToClaim = getPageBase().getWorkflowManager().isCurrentUserAuthorizedToClaim(getModelObject()); - return isAuthorizedToSubmit || isAuthorizedToClaim || isAuthorizedToDelegate; - } catch (Exception ex){ + return WebComponentUtil.runUnderPowerOfAttorneyIfNeeded(() -> + getPageBase().getWorkflowManager().isCurrentUserAuthorizedToSubmit(getModelObject(), task, result) || + getPageBase().getWorkflowManager().isCurrentUserAuthorizedToDelegate(getModelObject(), task, result) || + getPageBase().getWorkflowManager().isCurrentUserAuthorizedToClaim(getModelObject()), + getPowerDonor(), getPageBase(), task, result); + } catch (Exception ex) { LOGGER.error("Unable to check user authorization for workitem actions: {}", ex.getLocalizedMessage()); } return false; } + protected PrismObject getPowerDonor() { + return null; + } + // Expects that we deal with primary changes of the focus (i.e. not of projections) // Beware: returns the full object; regardless of the security settings public ObjectType getCaseFocusObject(CaseType caseType) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java index 0b8b1be97f2..b48a69829a1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java @@ -323,8 +323,8 @@ public F computeSecurityFilter(M } @Override - public MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, - String attorneyAuthorizationAction, PrismObject donor, Task task, + public MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, + String attorneyAuthorizationAction, PrismObject donor, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java index daf8d1b517d..a512f067ddc 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java @@ -120,6 +120,13 @@ public static void assertReferenceValues(PrismReference ref, String... oids) { } } + public static void assertReferenceValues(List refs, String... oids) { + assert oids.length == refs.size() : "Wrong number of values in "+refs+"; expected "+oids.length+" but was "+refs.size(); + for (String oid: oids) { + assertReferenceValue(refs, oid); + } + } + public static void assertReferenceValue(PrismReference ref, String oid) { for (PrismReferenceValue val: ref.getValues()) { if (oid.equals(val.getOid())) { @@ -129,6 +136,15 @@ public static void assertReferenceValue(PrismReference ref, String oid) { fail("Oid "+oid+" not found in reference "+ref); } + public static void assertReferenceValue(List refs, String oid) { + for (Referencable ref: refs) { + if (oid.equals(ref.getOid())) { + return; + } + } + fail("Oid "+oid+" not found among references "+refs); + } + public static void assertItems(PrismContainer object, int expectedNumberOfItems) { List values = (List)object.getValues(); if (expectedNumberOfItems == 0) { diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/CaseWorkItemUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/CaseWorkItemUtil.java index 1c2e8c2a6cf..7aa3cb016fa 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/CaseWorkItemUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/CaseWorkItemUtil.java @@ -64,10 +64,14 @@ public static CaseWorkItemType getWorkItem(CaseType aCase, long id) { return null; } - public static boolean isCaseWorkItemNotClosed(CaseWorkItemType workItem){ + public static boolean isCaseWorkItemNotClosed(CaseWorkItemType workItem) { return workItem != null && workItem.getCloseTimestamp() == null; } + public static boolean isCaseWorkItemClosed(CaseWorkItemType workItem) { + return workItem != null && workItem.getCloseTimestamp() != null; + } + public static boolean isWorkItemClaimable(CaseWorkItemType workItem){ return workItem != null && (workItem.getOriginalAssigneeRef() == null || StringUtils.isEmpty(workItem.getOriginalAssigneeRef().getOid())) && !doesAssigneeExist(workItem) && CollectionUtils.isNotEmpty(workItem.getCandidateRef()); diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java index 4f197bcfbf2..8d39288005c 100644 --- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java +++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.test.util; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import java.util.ArrayList; @@ -356,4 +357,30 @@ public static void assertInstanceOf(String message, Object object, Class expe private static PrismContext getPrismContext() { return TestSpringContextHolder.getPrismContext(); } + + public static void assertThatReferenceMatches(ObjectReferenceType ref, String desc, String expectedOid, QName expectedType) { + assertThat(ref).as(desc).isNotNull(); + assertThat(ref.getOid()).as(desc + ".oid").isEqualTo(expectedOid); + assertThatTypeMatches(ref.getType(), desc + ".type", expectedType); + } + + // here because of AssertJ dependency (consider moving) + public static void assertThatTypeMatches(QName actualType, String desc, QName expectedType) { + assertThat(actualType).as(desc) + .matches(t -> QNameUtil.match(t, expectedType), "matches " + expectedType); + } + + // here because of AssertJ dependency (consider moving) + public static void assertUriMatches(String current, String desc, QName expected) { + assertThat(current).as(desc) + .isNotNull() + .matches(s -> QNameUtil.match(QNameUtil.uriToQName(s, true), expected), "is " + expected); + } + + // here because of AssertJ dependency (consider moving) + public static void assertUriMatches(String current, String desc, String expected) { + assertThat(current).as(desc) + .isNotNull() + .matches(s -> QNameUtil.matchUri(s, expected), "is " + expected); + } } diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java index 1605fca7f9d..c081615fc6f 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java @@ -27,7 +27,10 @@ import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.enforcer.api.ItemSecurityConstraints; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.CheckedProducer; import com.evolveum.midpoint.util.DisplayableValue; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.Producer; import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ExecuteCredentialResetRequestType; @@ -151,7 +154,7 @@ ModelContext previewChanges( RoleSelectionSpecification getAssignableRoleSpecification(PrismObject assignmentHolder, Class targetType, int assignmentOrder, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException, SecurityViolationException; /** - * Returns filter for lookup of donors or power of attorney. The donors are the users that have granted + * Returns filter for lookup of donors of power of attorney. The donors are the users that have granted * the power of attorney to the currently logged-in user. * * TODO: authorization limitations @@ -352,12 +355,20 @@ List getDeputyAssignees(ObjectReferenceType assignee, QName */ ActivationStatusType getAssignmentEffectiveStatus(String lifecycleStatus, ActivationType activationType); - MidPointPrincipal assumePowerOfAttorney(PrismObject donor, Task task, OperationResult result) + MidPointPrincipal assumePowerOfAttorney(PrismObject donor, Task task, OperationResult result) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException; MidPointPrincipal dropPowerOfAttorney(Task task, OperationResult result) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException; + T runUnderPowerOfAttorney(Producer producer, PrismObject donor, Task task, OperationResult result) + throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException; + + default T runUnderPowerOfAttorneyChecked(CheckedProducer producer, PrismObject donor, Task task, OperationResult result) + throws CommonException { + return MiscUtil.runChecked((p) -> runUnderPowerOfAttorney(p, donor, task, result), producer); + } + // Maybe a bit of hack: used to deduplicate processing of localizable message templates @NotNull LocalizableMessageType createLocalizableMessageType(LocalizableMessageTemplateType template, diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java index 9c91af6cf58..8cebd183846 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java @@ -34,6 +34,7 @@ import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; import com.evolveum.midpoint.schema.util.*; import com.evolveum.midpoint.security.enforcer.api.FilterGizmo; +import com.evolveum.midpoint.util.*; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.BooleanUtils; @@ -128,13 +129,6 @@ import com.evolveum.midpoint.security.enforcer.api.ObjectSecurityConstraints; import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.DisplayableValue; -import com.evolveum.midpoint.util.LocalizableMessage; -import com.evolveum.midpoint.util.LocalizableMessageBuilder; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.CommunicationException; @@ -1507,7 +1501,7 @@ public ActivationStatusType getAssignmentEffectiveStatus(String lifecycleStatus, } @Override - public MidPointPrincipal assumePowerOfAttorney(PrismObject donor, Task task, OperationResult result) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException { + public MidPointPrincipal assumePowerOfAttorney(PrismObject donor, Task task, OperationResult result) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException { MidPointPrincipal attorneyPrincipal = securityContextManager.getPrincipal(); MidPointPrincipal donorPrincipal = securityEnforcer.createDonorPrincipal(attorneyPrincipal, ModelAuthorizationAction.ATTORNEY.getUrl(), donor, task, result); @@ -1550,6 +1544,19 @@ public MidPointPrincipal dropPowerOfAttorney(Task task, OperationResult result) return previousPrincipal; } + @Override + public T runUnderPowerOfAttorney(Producer producer, + PrismObject donor, Task task, OperationResult result) + throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, + CommunicationException, ConfigurationException { + assumePowerOfAttorney(donor, task, result); + try { + return producer.run(); + } finally { + dropPowerOfAttorney(task, result); + } + } + @Override @NotNull public LocalizableMessageType createLocalizableMessageType(LocalizableMessageTemplateType template, diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/sync/TestImportRecon.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/sync/TestImportRecon.java index 27079c14d62..f20e0cd2ca0 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/sync/TestImportRecon.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/sync/TestImportRecon.java @@ -94,7 +94,7 @@ public class TestImportRecon extends AbstractInitializedModelIntegrationTest { private static final String USER_AUGUSTUS_NAME = "augustus"; - private static class AccountTestResource extends TestResource { + private static class AccountTestResource extends TestResource { private final String name; @SuppressWarnings({ "FieldCanBeLocal", "unused" }) private final String fullName; diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index d1ddc86b3e1..c2105115073 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -5395,6 +5395,15 @@ protected String addAndRecompute(File file, Task task, OperationResult result) t return object.getOid(); } + protected String addAndRecompute(TestResource testResource, Task task, OperationResult result) throws Exception { + PrismObject object = repoAddObjectFromFile(testResource.file, result); + modelService.recompute(object.asObjectable().getClass(), object.getOid(), null, task, result); + PrismObject after = getObject(object.asObjectable().getClass(), object.getOid()); + display("Object: " + testResource.file, after); + testResource.object = after; + return after.getOid(); + } + protected void assertAuditReferenceValue(AuditEventRecord event, String refName, String oid, QName type, String name) { Set values = event.getReferenceValues(refName); assertEquals("Wrong # of reference values of '" + refName + "'", 1, values.size()); @@ -5938,6 +5947,12 @@ protected CaseAsserter assertCase(String oid, String message) throws Objec return asserter; } + protected CaseAsserter assertCase(CaseType aCase, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + CaseAsserter asserter = CaseAsserter.forCase(aCase.asPrismObject(), message); + initializeAsserter(asserter); + return asserter; + } + protected CaseAsserter assertCaseAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { CaseAsserter asserter = assertCase(oid, "after"); asserter.display(); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/engine/EngineInvocationContext.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/engine/EngineInvocationContext.java index 324b878ae47..ad25e833fa3 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/engine/EngineInvocationContext.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/engine/EngineInvocationContext.java @@ -25,10 +25,7 @@ import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.impl.engine.helpers.DelayedNotification; @@ -141,7 +138,8 @@ public void addAuditRecord(AuditEventRecord record) { } public void commit(OperationResult parentResult) - throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException { + throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException, + ExpressionEvaluationException, ConfigurationException, CommunicationException { OperationResult result = parentResult.subresult(OP_COMMIT) .setMinor() .build(); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/CaseOperationExecutionTaskHandler.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/CaseOperationExecutionTaskHandler.java index 52bde4cd729..fd908082711 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/CaseOperationExecutionTaskHandler.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/CaseOperationExecutionTaskHandler.java @@ -7,19 +7,15 @@ package com.evolveum.midpoint.wf.impl.execution; -import com.evolveum.midpoint.model.api.context.ModelProjectionContext; import com.evolveum.midpoint.model.impl.lens.Clockwork; import com.evolveum.midpoint.model.impl.lens.LensContext; -import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.repo.api.PreconditionViolationException; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.ObjectTreeDeltas; -import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.CaseTypeUtil; import com.evolveum.midpoint.task.api.*; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.LoggingUtils; @@ -29,7 +25,6 @@ import com.evolveum.midpoint.wf.impl.processors.primary.PcpGeneralHelper; import com.evolveum.midpoint.wf.impl.util.MiscHelper; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskPartitionDefinitionType; import org.springframework.beans.factory.annotation.Autowired; @@ -63,6 +58,7 @@ public class CaseOperationExecutionTaskHandler implements TaskHandler { @Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; + @Autowired private LensContextHelper lensContextHelper; @Override public TaskRunResult run(RunningTask task, TaskPartitionDefinitionType partitionDefinition) { @@ -109,7 +105,7 @@ private void executeLocalChanges(CaseType subcase, RunningTask task, OperationRe if (focusChange != null) { approvalMetadataHelper.addAssignmentApprovalMetadata(focusChange, subcase, task, result); } - mergeDeltasToModelContext(modelContext, singletonList(deltas)); + lensContextHelper.mergeDeltasToModelContext(modelContext, singletonList(deltas)); executeModelContext(modelContext, subcase, task, result); executionHelper.closeCaseInRepository(subcase, result); executionHelper.checkDependentCases(subcase.getParentRef().getOid(), result); @@ -119,78 +115,12 @@ private void executeAllChanges(CaseType rootCase, RunningTask task, OperationRes throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException, PolicyViolationException, PreconditionViolationException, ObjectAlreadyExistsException, SecurityViolationException { - LensContext modelContext = collectApprovedDeltasToModelContext(rootCase, task, result); + List subcases = miscHelper.getSubcases(rootCase, result); + LensContext modelContext = lensContextHelper.collectApprovedDeltasToModelContext(rootCase, subcases, task, result); executeModelContext(modelContext, rootCase, task, result); executionHelper.closeCaseInRepository(rootCase, result); } - private LensContext collectApprovedDeltasToModelContext(CaseType rootCase, RunningTask task, OperationResult result) - throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, - ExpressionEvaluationException { - List subcases = miscHelper.getSubcases(rootCase, result); - LensContext rootContext = (LensContext) miscHelper.getModelContext(rootCase, task, result); - List deltasToMerge = new ArrayList<>(); - - for (CaseType subcase : subcases) { - if (!CaseTypeUtil.isClosed(subcase)) { - throw new IllegalStateException("Child case " + subcase + " is not in CLOSED state; its state is " + subcase.getState()); - } - ObjectTreeDeltas deltas = pcpGeneralHelper.retrieveResultingDeltas(subcase); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Child case {} has {} resulting deltas", subcase, deltas != null ? deltas.getDeltaList().size() : 0); - } - if (deltas != null) { - ObjectDelta focusChange = deltas.getFocusChange(); - if (focusChange != null) { - approvalMetadataHelper.addAssignmentApprovalMetadata(focusChange, subcase, task, result); - } - if (focusChange != null && focusChange.isAdd()) { - deltasToMerge.add(0, deltas); // "add" must go first - } else { - deltasToMerge.add(deltas); - } - } - } - mergeDeltasToModelContext(rootContext, deltasToMerge); - return rootContext; - } - - private void mergeDeltasToModelContext(LensContext rootContext, List deltasToMerge) - throws SchemaException { - for (ObjectTreeDeltas deltaToMerge : deltasToMerge) { - LensFocusContext focusContext = rootContext.getFocusContext(); - ObjectDelta focusDelta = deltaToMerge.getFocusChange(); - if (focusDelta != null) { - LOGGER.trace("Adding delta to root model context; delta = {}", focusDelta.debugDumpLazily()); - if (focusContext.getPrimaryDelta() != null && !focusContext.getPrimaryDelta().isEmpty()) { - //noinspection unchecked - focusContext.addPrimaryDelta(focusDelta); - } else { - //noinspection unchecked - focusContext.setPrimaryDelta(focusDelta); - } - } - //noinspection unchecked - Set>> entries = deltaToMerge.getProjectionChangeMapEntries(); - for (Map.Entry> entry : entries) { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Adding projection delta to root model context; rsd = {}, delta = {}", entry.getKey(), - entry.getValue().debugDump()); - } - ModelProjectionContext projectionContext = rootContext.findProjectionContext(entry.getKey()); - if (projectionContext == null) { - // TODO more liberal treatment? - throw new IllegalStateException("No projection context for " + entry.getKey()); - } - if (projectionContext.getPrimaryDelta() != null && !projectionContext.getPrimaryDelta().isEmpty()) { - projectionContext.addPrimaryDelta(entry.getValue()); - } else { - projectionContext.setPrimaryDelta(entry.getValue()); - } - } - } - } - private void executeModelContext(LensContext modelContext, CaseType aCase, RunningTask task, OperationResult result) throws SchemaException, CommunicationException, ObjectNotFoundException, ObjectAlreadyExistsException, ConfigurationException, PreconditionViolationException, SecurityViolationException, PolicyViolationException, @@ -212,7 +142,7 @@ private void executeModelContext(LensContext modelContext, CaseType aCase, Ru while (projectionIterator.hasNext()) { LensProjectionContext projectionContext = projectionIterator.next(); if (!ObjectDelta.isEmpty(projectionContext.getPrimaryDelta()) || !ObjectDelta.isEmpty(projectionContext.getSyncDelta())) { - continue; // don't remove client requested or externally triggered actions! + continue; // don't remove client requested or externally triggered actions! } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Removing projection context {}", projectionContext.getHumanReadableName()); @@ -223,8 +153,6 @@ private void executeModelContext(LensContext modelContext, CaseType aCase, Ru task.setChannel(modelContext.getChannel()); } clockwork.run(modelContext, task, result); -// task.setModelOperationContext(context.toLensContextType(context.getState() == ModelState.FINAL)); -// task.flushPendingModifications(result); } @Override diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/ExecutionHelper.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/ExecutionHelper.java index c22c7a40ae5..bb4077b5492 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/ExecutionHelper.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/ExecutionHelper.java @@ -9,6 +9,7 @@ import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.common.SystemObjectCache; +import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ItemDelta; @@ -20,11 +21,11 @@ import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.CaseTypeUtil; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.task.api.TaskExecutionStatus; import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.impl.access.AuthorizationHelper; @@ -72,6 +73,7 @@ public class ExecutionHelper { @Autowired public WorkItemHelper workItemHelper; @Autowired public AuthorizationHelper authorizationHelper; @Autowired private SystemObjectCache systemObjectCache; + @Autowired private LensContextHelper lensContextHelper; private static final String DEFAULT_EXECUTION_GROUP_PREFIX_FOR_SERIALIZATION = "$approval-task-group$:"; private static final long DEFAULT_SERIALIZATION_RETRY_TIME = 10000L; @@ -86,15 +88,6 @@ public void closeCaseInRepository(CaseType aCase, OperationResult result) LOGGER.debug("Marked case {} as closed", aCase); } - public void setCaseStateInRepository(CaseType aCase, String newState, OperationResult result) - throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { - List> modifications = prismContext.deltaFor(CaseType.class) - .item(CaseType.F_STATE).replace(newState) - .asItemDeltas(); - repositoryService.modifyObject(CaseType.class, aCase.getOid(), modifications, result); - LOGGER.debug("Marked case {} as {}", aCase, newState); - } - /** * We need to check * 1) if there are any executable cases that depend on this one @@ -147,7 +140,7 @@ public void checkDependentCases(String rootOid, OperationResult result) } } - public void setExecutionConstraints(Task task, CaseType aCase, OperationResult result) throws SchemaException { + private void setExecutionConstraints(Task task, CaseType aCase, OperationResult result) throws SchemaException { PrismObject systemConfiguration = systemObjectCache.getSystemConfiguration(result); WfConfigurationType wfConfiguration = systemConfiguration != null ? systemConfiguration.asObjectable().getWorkflowConfiguration() : null; WfExecutionTasksConfigurationType tasksConfig = wfConfiguration != null ? wfConfiguration.getExecutionTasks() : null; @@ -225,4 +218,55 @@ private String getGroupSuffix(WfExecutionTasksSerializationScopeType scope, Case throw new AssertionError("Unknown scope: " + scope); } } + + public void submitExecutionTask(CaseType aCase, boolean waiting, OperationResult result) + throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + + // We must do this before the task is started, because as part of task completion we set state to CLOSED. + // So if we set state to EXECUTING after the task is started, the case might be already closed at that point. + // (If task is fast enough.) + markCaseAsExecuting(aCase, result); + + Task task = taskManager.createTaskInstance("execute"); + task.setName("Execution of " + aCase.getName().getOrig()); + task.setOwner(getExecutionTaskOwner(result)); + task.setObjectRef(ObjectTypeUtil.createObjectRef(aCase, prismContext)); + task.setHandlerUri(CaseOperationExecutionTaskHandler.HANDLER_URI); + if (waiting) { + task.setInitialExecutionStatus(TaskExecutionStatus.WAITING); + } + setExecutionConstraints(task, aCase, result); + taskManager.switchToBackground(task, result); + } + + private void markCaseAsExecuting(CaseType aCase, OperationResult result) + throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { + List> modifications = prismContext.deltaFor(CaseType.class) + .item(CaseType.F_STATE).replace(SchemaConstants.CASE_STATE_EXECUTING) + .asItemDeltas(); + repositoryService.modifyObject(CaseType.class, aCase.getOid(), modifications, result); + LOGGER.debug("Marked case {} as {}", aCase, SchemaConstants.CASE_STATE_EXECUTING); + } + + private PrismObject getExecutionTaskOwner(OperationResult result) throws SchemaException, ObjectNotFoundException { + return repositoryService.getObject(UserType.class, SystemObjectsType.USER_ADMINISTRATOR.value(), null, result); + } + + /** + * Checks that there are any deltas to be executed and if so, submits the execution task. + * @param rootCase Operation request case (always!) + * @param subcases List of subcases (must be current). + */ + public void submitExecutionTaskIfNeeded(CaseType rootCase, List subcases, Task task, OperationResult result) + throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, + ExpressionEvaluationException, ObjectAlreadyExistsException { + assert ObjectTypeUtil.hasArchetype(rootCase, SystemObjectsType.ARCHETYPE_OPERATION_REQUEST.value()); + LensContext lensContext = lensContextHelper.collectApprovedDeltasToModelContext(rootCase, subcases, task, result); + if (lensContext.hasAnyPrimaryChange()) { + submitExecutionTask(rootCase, false, result); + } else { + LOGGER.trace("No primary delta in 'approved' lens context, not submitting the execution task"); + closeCaseInRepository(rootCase, result); + } + } } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/LensContextHelper.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/LensContextHelper.java new file mode 100644 index 00000000000..8288a8ec986 --- /dev/null +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/execution/LensContextHelper.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2020 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.wf.impl.execution; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.evolveum.midpoint.task.api.Task; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.model.api.context.ModelProjectionContext; +import com.evolveum.midpoint.model.impl.lens.LensContext; +import com.evolveum.midpoint.model.impl.lens.LensFocusContext; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.ObjectTreeDeltas; +import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.CaseTypeUtil; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.wf.impl.processors.primary.ApprovalMetadataHelper; +import com.evolveum.midpoint.wf.impl.processors.primary.PcpGeneralHelper; +import com.evolveum.midpoint.wf.impl.util.MiscHelper; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; + +/** + * Helps with managing LensContext objects for approved changes execution. + */ +@Component +public class LensContextHelper { + + private static final Trace LOGGER = TraceManager.getTrace(LensContextHelper.class); + + @Autowired private MiscHelper miscHelper; + @Autowired private PcpGeneralHelper pcpGeneralHelper; + @Autowired private ApprovalMetadataHelper approvalMetadataHelper; + + LensContext collectApprovedDeltasToModelContext(CaseType rootCase, List subcases, Task task, OperationResult result) + throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, + ExpressionEvaluationException { + LensContext rootContext = (LensContext) miscHelper.getModelContext(rootCase, task, result); + List deltasToMerge = new ArrayList<>(); + + for (CaseType subcase : subcases) { + if (!CaseTypeUtil.isClosed(subcase)) { + throw new IllegalStateException("Child case " + subcase + " is not in CLOSED state; its state is " + subcase.getState()); + } + ObjectTreeDeltas deltas = pcpGeneralHelper.retrieveResultingDeltas(subcase); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Child case {} has {} resulting deltas", subcase, deltas != null ? deltas.getDeltaList().size() : 0); + } + if (deltas != null) { + ObjectDelta focusChange = deltas.getFocusChange(); + if (focusChange != null) { + approvalMetadataHelper.addAssignmentApprovalMetadata(focusChange, subcase, task, result); + } + if (focusChange != null && focusChange.isAdd()) { + deltasToMerge.add(0, deltas); // "add" must go first + } else { + deltasToMerge.add(deltas); + } + } + } + mergeDeltasToModelContext(rootContext, deltasToMerge); + return rootContext; + } + + void mergeDeltasToModelContext(LensContext rootContext, List deltasToMerge) + throws SchemaException { + for (ObjectTreeDeltas deltaToMerge : deltasToMerge) { + LensFocusContext focusContext = rootContext.getFocusContext(); + ObjectDelta focusDelta = deltaToMerge.getFocusChange(); + if (focusDelta != null) { + LOGGER.trace("Adding delta to root model context; delta = {}", focusDelta.debugDumpLazily()); + if (focusContext.getPrimaryDelta() != null && !focusContext.getPrimaryDelta().isEmpty()) { + //noinspection unchecked + focusContext.addPrimaryDelta(focusDelta); + } else { + //noinspection unchecked + focusContext.setPrimaryDelta(focusDelta); + } + } + //noinspection unchecked + Set>> entries = deltaToMerge.getProjectionChangeMapEntries(); + for (Map.Entry> entry : entries) { + LOGGER.trace("Adding projection delta to root model context; rsd = {}, delta = {}", entry.getKey(), + entry.getValue().debugDumpLazily()); + ModelProjectionContext projectionContext = rootContext.findProjectionContext(entry.getKey()); + if (projectionContext == null) { + // TODO more liberal treatment? + throw new IllegalStateException("No projection context for " + entry.getKey()); + } + if (projectionContext.getPrimaryDelta() != null && !projectionContext.getPrimaryDelta().isEmpty()) { + projectionContext.addPrimaryDelta(entry.getValue()); + } else { + projectionContext.setPrimaryDelta(entry.getValue()); + } + } + } + } +} diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ChangeProcessor.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ChangeProcessor.java index 3b3eb5b4599..3a6a17d7427 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ChangeProcessor.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ChangeProcessor.java @@ -82,7 +82,8 @@ HookOperationMode processModelInvocation(@NotNull ModelInvocationContext ctx, * @throws SchemaException */ void onProcessEnd(EngineInvocationContext ctx, OperationResult result) - throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException; + throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException, + ExpressionEvaluationException, ConfigurationException, CommunicationException; /** * Prepares a process instance-related audit record. diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java index f041c4f697e..17732b29c4a 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java @@ -27,9 +27,6 @@ import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.CaseTypeUtil; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskExecutionStatus; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.QNameUtil; @@ -41,7 +38,6 @@ import com.evolveum.midpoint.wf.impl.engine.EngineInvocationContext; import com.evolveum.midpoint.wf.impl.engine.WorkflowEngine; import com.evolveum.midpoint.wf.impl.engine.helpers.AuditHelper; -import com.evolveum.midpoint.wf.impl.execution.CaseOperationExecutionTaskHandler; import com.evolveum.midpoint.wf.impl.execution.ExecutionHelper; import com.evolveum.midpoint.wf.impl.processes.common.StageComputeHelper; import com.evolveum.midpoint.wf.impl.processors.*; @@ -323,7 +319,7 @@ private HookOperationMode executeStartInstructions(List ins if (case0 != null) { if (ModelExecuteOptions.isExecuteImmediatelyAfterApproval(ctx.modelContext.getOptions())) { - submitExecutionTask(case0, false, result); + executionHelper.submitExecutionTask(case0, false, result); } else { executionHelper.closeCaseInRepository(case0, result); } @@ -422,7 +418,7 @@ private LensContext contextCopyWithNoDelta(ModelContext context) { */ @Override public void onProcessEnd(EngineInvocationContext ctx, OperationResult result) - throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException { + throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, PreconditionViolationException, ExpressionEvaluationException, ConfigurationException, CommunicationException { CaseType currentCase = ctx.getCurrentCase(); ObjectTreeDeltas deltas = prepareDeltaOut(currentCase); @@ -463,7 +459,7 @@ public void onProcessEnd(EngineInvocationContext ctx, OperationResult result) } else { waiting = false; } - submitExecutionTask(currentCase, waiting, result); + executionHelper.submitExecutionTask(currentCase, waiting, result); } else { LOGGER.debug("Case {} is rejected (with immediate execution) -- nothing to do here", currentCase); executionHelper.closeCaseInRepository(currentCase, result); @@ -476,7 +472,7 @@ public void onProcessEnd(EngineInvocationContext ctx, OperationResult result) List subcases = miscHelper.getSubcases(rootCase, result); if (subcases.stream().allMatch(CaseTypeUtil::isClosed)) { LOGGER.debug("All subcases of {} are closed, so let's execute the deltas", rootCase); - submitExecutionTask(rootCase, false, result); + executionHelper.submitExecutionTaskIfNeeded(rootCase, subcases, ctx.getTask(), result); } else { LOGGER.debug("Some subcases of {} are not closed yet. Delta execution is therefore postponed.", rootCase); for (CaseType subcase : subcases) { @@ -488,30 +484,6 @@ public void onProcessEnd(EngineInvocationContext ctx, OperationResult result) } } - private void submitExecutionTask(CaseType aCase, boolean waiting, OperationResult result) - throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { - - // We must do this before the task is started, because as part of task completion we set state to CLOSED. - // So if we set state to EXECUTING after the task is started, the case might be already closed at that point. - // (If task is fast enough.) - executionHelper.setCaseStateInRepository(aCase, SchemaConstants.CASE_STATE_EXECUTING, result); - - Task task = taskManager.createTaskInstance("execute"); - task.setName("Execution of " + aCase.getName().getOrig()); - task.setOwner(getExecutionTaskOwner(result)); - task.setObjectRef(ObjectTypeUtil.createObjectRef(aCase, prismContext)); - task.setHandlerUri(CaseOperationExecutionTaskHandler.HANDLER_URI); - if (waiting) { - task.setInitialExecutionStatus(TaskExecutionStatus.WAITING); - } - executionHelper.setExecutionConstraints(task, aCase, result); - taskManager.switchToBackground(task, result); - } - - private PrismObject getExecutionTaskOwner(OperationResult result) throws SchemaException, ObjectNotFoundException { - return repositoryService.getObject(UserType.class, SystemObjectsType.USER_ADMINISTRATOR.value(), null, result); - } - private ObjectTreeDeltas prepareDeltaOut(CaseType aCase) throws SchemaException { ObjectTreeDeltas deltaIn = generalHelper.retrieveDeltasToApprove(aCase); if (ApprovalUtils.isApprovedFromUri(aCase.getOutcome())) { diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/AbstractWfTest.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/AbstractWfTest.java index 49a0d5d4616..359389a2a65 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/AbstractWfTest.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/AbstractWfTest.java @@ -31,6 +31,7 @@ import com.evolveum.midpoint.test.AbstractIntegrationTest; import com.evolveum.midpoint.test.Checker; import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.test.asserter.CaseAsserter; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.wf.api.WorkflowManager; import com.evolveum.midpoint.wf.impl.access.WorkItemManager; @@ -53,6 +54,7 @@ import java.util.List; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; /** @@ -356,4 +358,13 @@ public RelatedCases find(Task task, OperationResult result) throws SchemaExcepti return this; } } + + /** + * Takes case OID from the operation result (via asynchronous identifier). + */ + protected CaseAsserter assertCase(OperationResult result, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + String caseOid = OperationResult.referenceToCaseOid(result.findAsynchronousOperationReference()); + assertThat(caseOid).as("No background case OID").isNotNull(); + return assertCase(caseOid, message); + } } diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestMiscellaneous.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestMiscellaneous.java index 7b98a4328e3..b316cd19942 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestMiscellaneous.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestMiscellaneous.java @@ -15,6 +15,9 @@ import java.util.Collections; import java.util.List; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.test.TestResource; + import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Test; @@ -40,36 +43,26 @@ public class TestMiscellaneous extends AbstractWfTestPolicy { private static final File TEST_RESOURCE_DIR = new File("src/test/resources/miscellaneous"); - private static final File ROLE_SAILOR_FILE = new File(TEST_RESOURCE_DIR, "role-sailor.xml"); - private static final String ROLE_SAILOR_OID = "3ccc0a00-6a3b-4ae0-94a3-d45fc457f63f"; - - private static final File ROLE_CAPTAIN_FILE = new File(TEST_RESOURCE_DIR, "role-captain.xml"); - private static final String ROLE_CAPTAIN_OID = "15a99cf1-5886-44d4-8aaf-7e1f46ccec36"; - - private static final File USER_SCOTT_FILE = new File(TEST_RESOURCE_DIR, "user-scott.xml"); - private static final String USER_SCOTT_OID = "929c49ed-0100-4068-b8e4-137bd8ebd6b2"; - - private static final File METAROLE_PRIZE_FILE = new File(TEST_RESOURCE_DIR, "metarole-prize.xml"); - - private static final File METAROLE_APPROVE_UNASSIGN_FILE = new File(TEST_RESOURCE_DIR, "metarole-approve-unassign.xml"); + private static final TestResource ROLE_SAILOR = new TestResource<>(TEST_RESOURCE_DIR, "role-sailor.xml", "3ccc0a00-6a3b-4ae0-94a3-d45fc457f63f"); + private static final TestResource ROLE_CAPTAIN = new TestResource<>(TEST_RESOURCE_DIR, "role-captain.xml", "15a99cf1-5886-44d4-8aaf-7e1f46ccec36"); + private static final TestResource USER_SCOTT = new TestResource<>(TEST_RESOURCE_DIR, "user-scott.xml", "929c49ed-0100-4068-b8e4-137bd8ebd6b2"); - private static final File ROLE_GOLD_FILE = new File(TEST_RESOURCE_DIR, "role-gold.xml"); - private static final String ROLE_GOLD_OID = "0b3ad53e-7c1d-41d0-a447-ce94cd25c46a"; + private static final TestResource METAROLE_PRIZE = new TestResource<>(TEST_RESOURCE_DIR, "metarole-prize.xml", "2330f9df-83bc-4270-86fc-27fca2b616a7"); + private static final TestResource METAROLE_APPROVE_UNASSIGN = new TestResource<>(TEST_RESOURCE_DIR, "metarole-approve-unassign.xml", "e5144353-c39d-445c-bf15-c4b80ce75918"); - private static final File ROLE_SILVER_FILE = new File(TEST_RESOURCE_DIR, "role-silver.xml"); - private static final String ROLE_SILVER_OID = "ee5206f8-930a-4c85-bfee-c16e4462df23"; + private static final TestResource ROLE_GOLD = new TestResource<>(TEST_RESOURCE_DIR, "role-gold.xml", "0b3ad53e-7c1d-41d0-a447-ce94cd25c46a"); + private static final TestResource ROLE_SILVER = new TestResource<>(TEST_RESOURCE_DIR, "role-silver.xml", "ee5206f8-930a-4c85-bfee-c16e4462df23"); + private static final TestResource ROLE_BRONZE = new TestResource<>(TEST_RESOURCE_DIR, "role-bronze.xml", "f16f4dd7-2830-4d0a-b6ed-9fbf253dbaf3"); - private static final File ROLE_BRONZE_FILE = new File(TEST_RESOURCE_DIR, "role-bronze.xml"); - //private static final String ROLE_BRONZE_OID = "f16f4dd7-2830-4d0a-b6ed-9fbf253dbaf3"; + private static final TestResource TEMPLATE_ASSIGNING_CAPTAIN = new TestResource<>(TEST_RESOURCE_DIR, "template-assigning-captain.xml", "18ac3da2-f2fa-496a-8e54-789a090ff492"); + private static final TestResource TEMPLATE_ASSIGNING_CAPTAIN_AFTER = new TestResource<>(TEST_RESOURCE_DIR, "template-assigning-captain-after.xml", "ace5d8f0-f54b-4f1b-92c0-8fa104a8fe84"); + private static final TestResource ROLE_ASSIGNING_CAPTAIN = new TestResource<>(TEST_RESOURCE_DIR, "role-assigning-captain.xml", "4bdd7ccc-8c52-41ff-a975-0313ec788507"); - private static final File TEMPLATE_ASSIGNING_CAPTAIN_FILE = new File(TEST_RESOURCE_DIR, "template-assigning-captain.xml"); - private static final String TEMPLATE_ASSIGNING_CAPTAIN_OID = "18ac3da2-f2fa-496a-8e54-789a090ff492"; - - private static final File TEMPLATE_ASSIGNING_CAPTAIN_AFTER_FILE = new File(TEST_RESOURCE_DIR, "template-assigning-captain-after.xml"); - private static final String TEMPLATE_ASSIGNING_CAPTAIN_AFTER_OID = "ace5d8f0-f54b-4f1b-92c0-8fa104a8fe84"; - - private static final File ROLE_ASSIGNING_CAPTAIN_FILE = new File(TEST_RESOURCE_DIR, "role-assigning-captain.xml"); - private static final String ROLE_ASSIGNING_CAPTAIN_OID = "4bdd7ccc-8c52-41ff-a975-0313ec788507"; + private static final TestResource USER_SCROOGE = new TestResource<>(TEST_RESOURCE_DIR, "user-scrooge.xml", "edf53304-2da0-4a7c-82b4-74fe35dcbc6e"); + private static final TestResource USER_GIZMODUCK = new TestResource<>(TEST_RESOURCE_DIR, "user-gizmoduck.xml", "6d0a7fce-b698-4f1d-95ce-14246452add5"); + private static final TestResource USER_LAUNCHPAD = new TestResource<>(TEST_RESOURCE_DIR, "user-launchpad.xml", "00880478-d006-4fc8-9d3a-87b5ec546c40"); + private static final TestResource ROLE_VAULT_ACCESS = new TestResource<>(TEST_RESOURCE_DIR, "role-vault-access.xml", "f6f95936-8714-4c7d-abdf-6cd3e6d2d6cc"); + private static final TestResource ROLE_ACCOUNTANT = new TestResource<>(TEST_RESOURCE_DIR, "role-accountant.xml", "5653fc70-3007-4f62-82dd-a36e0673505b"); @Override protected PrismObject getDefaultActor() { @@ -80,20 +73,26 @@ protected PrismObject getDefaultActor() { public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); - repoAddObjectFromFile(ROLE_SAILOR_FILE, initResult); - repoAddObjectFromFile(ROLE_CAPTAIN_FILE, initResult); + repoAdd(ROLE_SAILOR, initResult); + repoAdd(ROLE_CAPTAIN, initResult); + + repoAdd(METAROLE_PRIZE, initResult); + repoAdd(METAROLE_APPROVE_UNASSIGN, initResult); + repoAdd(ROLE_GOLD, initResult); + repoAdd(ROLE_SILVER, initResult); + repoAdd(ROLE_BRONZE, initResult); - repoAddObjectFromFile(METAROLE_PRIZE_FILE, initResult); - repoAddObjectFromFile(METAROLE_APPROVE_UNASSIGN_FILE, initResult); - repoAddObjectFromFile(ROLE_GOLD_FILE, initResult); - repoAddObjectFromFile(ROLE_SILVER_FILE, initResult); - repoAddObjectFromFile(ROLE_BRONZE_FILE, initResult); + addAndRecompute(USER_SCOTT, initTask, initResult); - addAndRecompute(USER_SCOTT_FILE, initTask, initResult); + repoAdd(TEMPLATE_ASSIGNING_CAPTAIN, initResult); + repoAdd(TEMPLATE_ASSIGNING_CAPTAIN_AFTER, initResult); + repoAdd(ROLE_ASSIGNING_CAPTAIN, initResult); - repoAddObjectFromFile(TEMPLATE_ASSIGNING_CAPTAIN_FILE, initResult); - repoAddObjectFromFile(TEMPLATE_ASSIGNING_CAPTAIN_AFTER_FILE, initResult); - repoAddObjectFromFile(ROLE_ASSIGNING_CAPTAIN_FILE, initResult); + repoAdd(ROLE_VAULT_ACCESS, initResult); + repoAdd(ROLE_ACCOUNTANT, initResult); + addAndRecompute(USER_SCROOGE, initTask, initResult); + addAndRecompute(USER_GIZMODUCK, initTask, initResult); + addAndRecompute(USER_LAUNCHPAD, initTask, initResult); } @Test @@ -111,11 +110,11 @@ public void test100RequesterComment() throws Exception { final String REQUESTER_COMMENT = "req.comment"; businessContext.setComment(REQUESTER_COMMENT); - ObjectDelta userDelta = createAssignmentUserDelta(userJackOid, ROLE_SAILOR_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + ObjectDelta userDelta = createAssignmentUserDelta(userJackOid, ROLE_SAILOR.oid, RoleType.COMPLEX_TYPE, null, null, null, true); Collection> deltas = MiscSchemaUtil.createCollection(userDelta); modelService.executeChanges(deltas, ModelExecuteOptions.createRequestBusinessContext(businessContext), task, result); - assertNotAssignedRole(userJackOid, ROLE_SAILOR_OID, result); + assertNotAssignedRole(userJackOid, ROLE_SAILOR.oid, result); CaseWorkItemType workItem = getWorkItem(task, result); display("Work item", workItem); @@ -139,7 +138,7 @@ public void test100RequesterComment() throws Exception { display("Event 2", event2); assertNotNull("Original assignee is null", event2.getOriginalAssigneeRef()); - assertEquals("Wrong original assignee OID", USER_SCOTT_OID, event2.getOriginalAssigneeRef().getOid()); + assertEquals("Wrong original assignee OID", USER_SCOTT.oid, event2.getOriginalAssigneeRef().getOid()); displayDumpable("audit", dummyAuditService); List records = dummyAuditService.getRecordsOfType(AuditEventType.WORKFLOW_PROCESS_INSTANCE); @@ -153,7 +152,7 @@ public void test100RequesterComment() throws Exception { CaseType parentCase = getCase(aCase.getParentRef().getOid()); waitForCaseClose(parentCase); - AssignmentType assignment = assertAssignedRole(userJackOid, ROLE_SAILOR_OID, result); + AssignmentType assignment = assertAssignedRole(userJackOid, ROLE_SAILOR.oid, result); display("assignment after creation", assignment); MetadataType metadata = assignment.getMetadata(); assertNotNull("Null request timestamp in metadata", metadata.getRequestTimestamp()); @@ -176,13 +175,13 @@ public void test105RequesterCommentImmediate() throws Exception { final String REQUESTER_COMMENT = "req.comment"; businessContext.setComment(REQUESTER_COMMENT); - ObjectDelta userDelta = createAssignmentUserDelta(userJackOid, ROLE_CAPTAIN_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + ObjectDelta userDelta = createAssignmentUserDelta(userJackOid, ROLE_CAPTAIN.oid, RoleType.COMPLEX_TYPE, null, null, null, true); Collection> deltas = MiscSchemaUtil.createCollection(userDelta); ModelExecuteOptions options = ModelExecuteOptions.createRequestBusinessContext(businessContext); options.setExecuteImmediatelyAfterApproval(true); modelService.executeChanges(deltas, options, task, result); - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); CaseWorkItemType workItem = getWorkItem(task, result); display("Work item", workItem); @@ -206,7 +205,7 @@ public void test105RequesterCommentImmediate() throws Exception { display("Event 2", event2); assertNotNull("Original assignee is null", event2.getOriginalAssigneeRef()); - assertEquals("Wrong original assignee OID", USER_SCOTT_OID, event2.getOriginalAssigneeRef().getOid()); + assertEquals("Wrong original assignee OID", USER_SCOTT.oid, event2.getOriginalAssigneeRef().getOid()); displayDumpable("audit", dummyAuditService); List records = dummyAuditService.getRecordsOfType(AuditEventType.WORKFLOW_PROCESS_INSTANCE); @@ -220,7 +219,7 @@ public void test105RequesterCommentImmediate() throws Exception { CaseType parentCase = getCase(aCase.getParentRef().getOid()); waitForCaseClose(parentCase); - AssignmentType assignment = assertAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + AssignmentType assignment = assertAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); display("assignment after creation", assignment); MetadataType metadata = assignment.getMetadata(); assertNotNull("Null request timestamp in metadata", metadata.getRequestTimestamp()); @@ -239,18 +238,18 @@ public void test110RequestPrunedRole() throws Exception { ModelExecuteOptions options = ModelExecuteOptions .createPartialProcessing(new PartialProcessingOptionsType().approvals(PartialProcessingTypeType.SKIP)); - assignRole(userJackOid, ROLE_GOLD_OID, options, task, result); - assertAssignedRole(getUser(userJackOid), ROLE_GOLD_OID); + assignRole(userJackOid, ROLE_GOLD.oid, options, task, result); + assertAssignedRole(getUser(userJackOid), ROLE_GOLD.oid); // WHEN - assignRole(userJackOid, ROLE_SILVER_OID, task, result); + assignRole(userJackOid, ROLE_SILVER.oid, task, result); // THEN result.computeStatus(); TestUtil.assertInProgress("Operation NOT in progress", result); - assertNotAssignedRole(userJackOid, ROLE_SILVER_OID, result); + assertNotAssignedRole(userJackOid, ROLE_SILVER.oid, result); // complete the work item related to assigning role silver CaseWorkItemType workItem = getWorkItem(task, result); @@ -263,7 +262,7 @@ public void test110RequestPrunedRole() throws Exception { CaseType rootCase = getCase(aCase.getParentRef().getOid()); waitForCaseClose(rootCase); - assertNotAssignedRole(userJackOid, ROLE_GOLD_OID, result); // should be pruned without approval + assertNotAssignedRole(userJackOid, ROLE_GOLD.oid, result); // should be pruned without approval } @Test @@ -274,9 +273,9 @@ public void test200GetRoleByTemplate() throws Exception { OperationResult result = getTestOperationResult(); // GIVEN - setDefaultUserTemplate(TEMPLATE_ASSIGNING_CAPTAIN_OID); + setDefaultUserTemplate(TEMPLATE_ASSIGNING_CAPTAIN.oid); unassignAllRoles(userJackOid); - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); // WHEN // some innocent change @@ -286,7 +285,7 @@ public void test200GetRoleByTemplate() throws Exception { result.computeStatus(); TestUtil.assertSuccess(result); - assertAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); } @Test @@ -297,9 +296,9 @@ public void test210GetRoleByTemplateAfterAssignments() throws Exception { OperationResult result = getTestOperationResult(); // GIVEN - setDefaultUserTemplate(TEMPLATE_ASSIGNING_CAPTAIN_AFTER_OID); + setDefaultUserTemplate(TEMPLATE_ASSIGNING_CAPTAIN_AFTER.oid); unassignAllRoles(userJackOid); - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); // WHEN // some innocent change @@ -310,7 +309,7 @@ public void test210GetRoleByTemplateAfterAssignments() throws Exception { result.computeStatus(); TestUtil.assertSuccess(result); - assertAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); } @Test @@ -323,16 +322,16 @@ public void test220GetRoleByFocusMappings() throws Exception { // GIVEN setDefaultUserTemplate(null); unassignAllRoles(userJackOid); - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); // WHEN - assignRole(userJackOid, ROLE_ASSIGNING_CAPTAIN_OID, task, result); + assignRole(userJackOid, ROLE_ASSIGNING_CAPTAIN.oid, task, result); // THEN result.computeStatus(); TestUtil.assertSuccess(result); - assertAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); } @Test @@ -345,13 +344,13 @@ public void test250SkippingApprovals() throws Exception { // GIVEN setDefaultUserTemplate(null); unassignAllRoles(userJackOid); - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); // WHEN ObjectDelta delta = prismContext.deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT) - .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN_OID, ObjectTypes.ROLE, prismContext)) + .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN.oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); ModelExecuteOptions options = ModelExecuteOptions.createPartialProcessing( new PartialProcessingOptionsType().approvals(PartialProcessingTypeType.SKIP)); @@ -361,7 +360,7 @@ public void test250SkippingApprovals() throws Exception { result.computeStatus(); TestUtil.assertSuccess(result); - assertAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); } /** @@ -376,24 +375,23 @@ public void test300DeleteRequestCase() throws Exception { OperationResult result = getTestOperationResult(); unassignAllRoles(userJackOid); - when(); - // @formatter:off ObjectDelta delta = - prismContext.deltaFor(UserType.class) + deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT) - .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN_OID, ObjectTypes.ROLE, prismContext)) + .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN.oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); // @formatter:on executeChanges(delta, null, task, result); - - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); RelatedCases relatedCases = new RelatedCases().find(task, result); CaseType approvalCase = relatedCases.getApprovalCase(); CaseType requestCase = relatedCases.getRequestCase(); + when(); + deleteObject(CaseType.class, requestCase.getOid(), task, result); then(); @@ -414,29 +412,189 @@ public void test310DeleteRequestCaseRaw() throws Exception { OperationResult result = getTestOperationResult(); unassignAllRoles(userJackOid); - when(); - // @formatter:off ObjectDelta delta = - prismContext.deltaFor(UserType.class) + deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT) - .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN_OID, ObjectTypes.ROLE, prismContext)) + .add(ObjectTypeUtil.createAssignmentTo(ROLE_CAPTAIN.oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); // @formatter:on executeChanges(delta, null, task, result); - - assertNotAssignedRole(userJackOid, ROLE_CAPTAIN_OID, result); + assertNotAssignedRole(userJackOid, ROLE_CAPTAIN.oid, result); RelatedCases relatedCases = new RelatedCases().find(task, result); CaseType approvalCase = relatedCases.getApprovalCase(); CaseType requestCase = relatedCases.getRequestCase(); + when(); + deleteObjectRaw(CaseType.class, requestCase.getOid(), task, result); then(); assertObjectDoesntExist(CaseType.class, requestCase.getOid()); assertObjectExists(CaseType.class, approvalCase.getOid()); + + // just to clean up before downstream tests + deleteObjectRaw(CaseType.class, approvalCase.getOid(), task, result); } + + @Test + public void test350ApproveAsAttorneyAdministrator() throws Exception { + given(); + + login(userAdministrator); + Task task = getTestTask(); + OperationResult result = getTestOperationResult(); + + // @formatter:off + ObjectDelta delta = + deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(ROLE_VAULT_ACCESS.oid, ObjectTypes.ROLE, prismContext)) + .asObjectDelta(USER_LAUNCHPAD.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + assertNotAssignedRole(USER_LAUNCHPAD.oid, ROLE_VAULT_ACCESS.oid, result); + + CaseWorkItemType workItem = getWorkItem(task, result); + + when(); + + modelInteractionService.runUnderPowerOfAttorneyChecked(() -> { + AbstractWorkItemOutputType output = new AbstractWorkItemOutputType(prismContext) + .outcome(SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT); + workflowService.completeWorkItem(CaseWorkItemUtil.getId(workItem), output, task, result); + return null; + }, USER_SCROOGE.object, task, result); + + then(); + + // @formatter:off + assertNotAssignedRole(USER_LAUNCHPAD.oid, ROLE_VAULT_ACCESS.oid, result); + CaseType approvalCase = assertCase(result, "after") + .display() + .assertOperationRequestArchetype() + .assertObjectRef(USER_LAUNCHPAD.oid, UserType.COMPLEX_TYPE) + .assertClosed() + .subcases() + .assertSubcases(1) + .single() + .getObject().asObjectable(); + + assertCase(approvalCase, "after") + .display() + .assertNameOrig("Assigning role \"vault-access\" to user \"launchpad\"") + .assertApprovalCaseArchetype() + .assertObjectRef(USER_LAUNCHPAD.oid, UserType.COMPLEX_TYPE) + .assertTargetRef(ROLE_VAULT_ACCESS.oid, RoleType.COMPLEX_TYPE) + .assertClosed() + .assertRejected() + .assertStageNumber(1) + .events() + .assertEvents(2) + .ofType(CaseCreationEventType.class) + .assertInitiatorRef(USER_ADMINISTRATOR_OID, UserType.COMPLEX_TYPE) + .end() + .ofType(WorkItemCompletionEventType.class) + .assertOriginalAssigneeRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertInitiatorRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertAttorneyRef(USER_ADMINISTRATOR_OID, UserType.COMPLEX_TYPE) + .end() + .end() + .workItems() + .assertWorkItems(1) + .single() + .assertNameOrig("Assigning role \"vault-access\" to user \"launchpad\"") + .assertStageNumber(1) + .assertOriginalAssigneeRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertPerformerRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) // we should perhaps list attorney here as well + .assertClosed() + .assertRejected(); + // @formatter:on + } + + @Test + public void test360ApproveAsAttorneyGizmoduck() throws Exception { + given(); + + login(userAdministrator); + Task task = getTestTask(); + OperationResult result = getTestOperationResult(); + + // @formatter:off + ObjectDelta delta = + deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(ROLE_VAULT_ACCESS.oid, ObjectTypes.ROLE, prismContext)) + .asObjectDelta(USER_LAUNCHPAD.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + assertNotAssignedRole(USER_LAUNCHPAD.oid, ROLE_VAULT_ACCESS.oid, result); + + CaseWorkItemType workItem = getWorkItem(task, result); + + when(); + + login(USER_GIZMODUCK.object); + + modelInteractionService.runUnderPowerOfAttorneyChecked(() -> { + AbstractWorkItemOutputType output = new AbstractWorkItemOutputType(prismContext) + .outcome(SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT); + workflowService.completeWorkItem(CaseWorkItemUtil.getId(workItem), output, task, result); + return null; + }, USER_SCROOGE.object, task, result); + + then(); + + login(userAdministrator); // to avoid problems because of insufficient privileges + + // @formatter:off + assertNotAssignedRole(USER_LAUNCHPAD.oid, ROLE_VAULT_ACCESS.oid, result); + CaseType approvalCase = assertCase(result, "after") + .display() + .assertOperationRequestArchetype() + .assertObjectRef(USER_LAUNCHPAD.oid, UserType.COMPLEX_TYPE) + .assertClosed() + .subcases() + .assertSubcases(1) + .single() + .getObject().asObjectable(); + + assertCase(approvalCase, "after") + .display() + .assertNameOrig("Assigning role \"vault-access\" to user \"launchpad\"") + .assertApprovalCaseArchetype() + .assertObjectRef(USER_LAUNCHPAD.oid, UserType.COMPLEX_TYPE) + .assertTargetRef(ROLE_VAULT_ACCESS.oid, RoleType.COMPLEX_TYPE) + .assertClosed() + .assertRejected() + .assertStageNumber(1) + .events() + .assertEvents(2) + .ofType(CaseCreationEventType.class) + .assertInitiatorRef(USER_ADMINISTRATOR_OID, UserType.COMPLEX_TYPE) + .end() + .ofType(WorkItemCompletionEventType.class) + .assertOriginalAssigneeRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertInitiatorRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertAttorneyRef(USER_GIZMODUCK.oid, UserType.COMPLEX_TYPE) + .end() + .end() + .workItems() + .assertWorkItems(1) + .single() + .assertNameOrig("Assigning role \"vault-access\" to user \"launchpad\"") + .assertStageNumber(1) + .assertOriginalAssigneeRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) + .assertPerformerRef(USER_SCROOGE.oid, UserType.COMPLEX_TYPE) // we should perhaps list attorney here as well + .assertClosed() + .assertRejected(); + + // @formatter:on + } + } diff --git a/model/workflow-impl/src/test/resources/miscellaneous/role-accountant.xml b/model/workflow-impl/src/test/resources/miscellaneous/role-accountant.xml new file mode 100644 index 00000000000..9b9a190ff4c --- /dev/null +++ b/model/workflow-impl/src/test/resources/miscellaneous/role-accountant.xml @@ -0,0 +1,24 @@ + + + + accountant + + attorney-scrooge-workitems + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#attorney + + UserType + + + name + scrooge + + + + + diff --git a/model/workflow-impl/src/test/resources/miscellaneous/role-vault-access.xml b/model/workflow-impl/src/test/resources/miscellaneous/role-vault-access.xml new file mode 100644 index 00000000000..7c8d65b752f --- /dev/null +++ b/model/workflow-impl/src/test/resources/miscellaneous/role-vault-access.xml @@ -0,0 +1,11 @@ + + + + vault-access + diff --git a/model/workflow-impl/src/test/resources/miscellaneous/user-gizmoduck.xml b/model/workflow-impl/src/test/resources/miscellaneous/user-gizmoduck.xml new file mode 100644 index 00000000000..386e56d7147 --- /dev/null +++ b/model/workflow-impl/src/test/resources/miscellaneous/user-gizmoduck.xml @@ -0,0 +1,16 @@ + + + + + gizmoduck + + + + Fenton Crackshell + diff --git a/model/workflow-impl/src/test/resources/miscellaneous/user-launchpad.xml b/model/workflow-impl/src/test/resources/miscellaneous/user-launchpad.xml new file mode 100644 index 00000000000..50b829f28da --- /dev/null +++ b/model/workflow-impl/src/test/resources/miscellaneous/user-launchpad.xml @@ -0,0 +1,12 @@ + + + + launchpad + Launchpad McQuack + diff --git a/model/workflow-impl/src/test/resources/miscellaneous/user-scrooge.xml b/model/workflow-impl/src/test/resources/miscellaneous/user-scrooge.xml new file mode 100644 index 00000000000..c7c6f514d94 --- /dev/null +++ b/model/workflow-impl/src/test/resources/miscellaneous/user-scrooge.xml @@ -0,0 +1,16 @@ + + + + + scrooge + + + + Scrooge McDuck + diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 48851114d52..8a7b1a268c4 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -431,7 +431,9 @@ protected PrismObject repoAddObjectFromFile(File file, protected PrismObject repoAdd(TestResource resource, OperationResult parentResult) throws SchemaException, ObjectAlreadyExistsException, IOException, EncryptionException { - return repoAddObjectFromFile(resource.file, parentResult); + PrismObject object = repoAddObjectFromFile(resource.file, parentResult); + resource.object = object; + return object; } protected PrismObject repoAddObjectFromFile( @@ -2729,6 +2731,7 @@ protected void assertRelationDef(List relations, QName q protected void initializeAsserter(AbstractAsserter asserter) { asserter.setPrismContext(prismContext); asserter.setObjectResolver(repoSimpleObjectResolver); + asserter.setRepositoryService(repositoryService); asserter.setProtector(protector); asserter.setClock(clock); } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/DummyTestResource.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/DummyTestResource.java index d81996abcf5..43bd17ce0ed 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/DummyTestResource.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/DummyTestResource.java @@ -8,6 +8,7 @@ package com.evolveum.midpoint.test; import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import java.io.File; @@ -15,7 +16,7 @@ * Representation of Dummy Resource in tests. */ @Experimental -public class DummyTestResource extends TestResource { +public class DummyTestResource extends TestResource { public final String name; public DummyResourceContoller controller; diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java index aaf0a754e97..27060acd216 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java @@ -7,7 +7,9 @@ package com.evolveum.midpoint.test; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import java.io.File; @@ -15,13 +17,18 @@ * Representation of any prism object in tests. */ @Experimental -public class TestResource { +public class TestResource { public final File file; public final String oid; + public PrismObject object; public TestResource(File dir, String fileName, String oid) { this.file = new File(dir, fileName); this.oid = oid; } + + public String getNameOrig() { + return object.getName().getOrig(); + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java index 058f0a86b4e..df6386ed07f 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.test.asserter; +import com.evolveum.midpoint.repo.api.RepositoryService; + import org.testng.AssertJUnit; import com.evolveum.midpoint.common.Clock; @@ -28,6 +30,7 @@ public abstract class AbstractAsserter { private RA returnAsserter; private PrismContext prismContext; private SimpleObjectResolver objectResolver; + private RepositoryService repositoryService; private Protector protector; private Clock clock; @@ -62,6 +65,14 @@ public void setObjectResolver(SimpleObjectResolver objectResolver) { this.objectResolver = objectResolver; } + public RepositoryService getRepositoryService() { + return repositoryService; + } + + public void setRepositoryService(RepositoryService repositoryService) { + this.repositoryService = repositoryService; + } + protected Protector getProtector() { return protector; } @@ -118,6 +129,7 @@ protected PrismObject resolveObject(Class type, Str protected void copySetupTo(AbstractAsserter other) { other.setPrismContext(this.getPrismContext()); other.setObjectResolver(this.getObjectResolver()); + other.setRepositoryService(this.getRepositoryService()); other.setProtector(this.getProtector()); other.setClock(this.getClock()); } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseAsserter.java index f42b8624b39..518efb38c1d 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseAsserter.java @@ -7,13 +7,25 @@ package com.evolveum.midpoint.test.asserter; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.schema.SearchResultList; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.test.asserter.prism.PolyStringAsserter; import com.evolveum.midpoint.test.asserter.prism.PrismObjectAsserter; +import com.evolveum.midpoint.test.util.MidPointAsserts; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; + +import javax.xml.namespace.QName; + +import static com.evolveum.midpoint.prism.PrismObject.asObjectableList; +import static org.assertj.core.api.Assertions.assertThat; /** * @author semancik - * */ public class CaseAsserter extends PrismObjectAsserter { @@ -70,6 +82,11 @@ public CaseAsserter assertName(String expectedOrig) { return this; } + @Override + public CaseAsserter assertNameOrig(String expectedOrig) { + return (CaseAsserter) super.assertNameOrig(expectedOrig); + } + @Override public CaseAsserter assertDescription(String expected) { super.assertDescription(expected); @@ -119,6 +136,7 @@ public CaseAsserter display(String message) { @Override public PolyStringAsserter> name() { + //noinspection unchecked return (PolyStringAsserter>)super.name(); } @@ -127,4 +145,92 @@ public CaseAsserter assertNoTrigger() { super.assertNoTrigger(); return this; } + + @Override + public CaseAsserter assertArchetypeRef(String expectedArchetypeOid) { + return (CaseAsserter) super.assertArchetypeRef(expectedArchetypeOid); + } + + @Override + public CaseAsserter assertNoArchetypeRef() { + return (CaseAsserter) super.assertNoArchetypeRef(); + } + + public CaseAsserter assertOperationRequestArchetype() { + return assertArchetypeRef(SystemObjectsType.ARCHETYPE_OPERATION_REQUEST.value()); + } + + public CaseAsserter assertApprovalCaseArchetype() { + return assertArchetypeRef(SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value()); + } + + public CaseAsserter assertClosed() { + return assertState(SchemaConstants.CASE_STATE_CLOSED_QNAME); + } + + private CaseAsserter assertState(QName expected) { + MidPointAsserts.assertUriMatches(getObjectable().getState(), "state", expected); + return this; + } + + public CaseAsserter assertApproved() { + return assertOutcome(SchemaConstants.MODEL_APPROVAL_OUTCOME_APPROVE); + } + + public CaseAsserter assertRejected() { + return assertOutcome(SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT); + } + + private CaseAsserter assertOutcome(String expected) { + MidPointAsserts.assertUriMatches(getObjectable().getOutcome(), "outcome", expected); + return this; + } + + public SubcasesAsserter subcases() { + OperationResult result = new OperationResult(CaseAsserter.class.getName() + ".subcases"); + ObjectQuery query = getPrismContext().queryFor(CaseType.class) + .item(CaseType.F_PARENT_REF).ref(getOid()) + .build(); + SearchResultList> subcases; + try { + subcases = getRepositoryService().searchObjects(CaseType.class, query, null, result); + } catch (SchemaException e) { + throw new AssertionError(e); + } + SubcasesAsserter asserter = new SubcasesAsserter<>(this, asObjectableList(subcases), getDetails()); + copySetupTo(asserter); + return asserter; + } + + public CaseAsserter assertObjectRef(String oid, QName typeName) { + return assertReference(getObjectable().getObjectRef(), "objectRef", oid, typeName); + } + + public CaseAsserter assertTargetRef(String oid, QName typeName) { + return assertReference(getObjectable().getTargetRef(), "targetRef", oid, typeName); + } + + private CaseAsserter assertReference(ObjectReferenceType ref, String desc, String oid, QName typeName) { + MidPointAsserts.assertThatReferenceMatches(ref, desc, oid, typeName); + return this; + } + + public CaseAsserter assertStageNumber(int expected) { + assertThat(getObjectable().getStageNumber()) + .as("stage number") + .isEqualTo(expected); + return this; + } + + public CaseEventsAsserter events() { + CaseEventsAsserter asserter = new CaseEventsAsserter<>(this, getObjectable().getEvent(), getDetails()); + copySetupTo(asserter); + return asserter; + } + + public CaseWorkItemsAsserter workItems() { + CaseWorkItemsAsserter asserter = new CaseWorkItemsAsserter<>(this, getObjectable().getWorkItem(), getDetails()); + copySetupTo(asserter); + return asserter; + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventAsserter.java new file mode 100644 index 00000000000..21f1b38efd1 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventAsserter.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.test.asserter.prism.PrismContainerValueAsserter; +import com.evolveum.midpoint.test.util.MidPointAsserts; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseEventType; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemEventType; + +import org.jetbrains.annotations.NotNull; + +/** + * Asserts about CaseEventType. + */ +public class CaseEventAsserter extends PrismContainerValueAsserter { + + public CaseEventAsserter(CaseEventType event) { + //noinspection unchecked + super(event.asPrismContainerValue()); + } + + public CaseEventAsserter(CaseEventType event, String details) { + //noinspection unchecked + super(event.asPrismContainerValue(), details); + } + + public CaseEventAsserter(CaseEventType event, RA returnAsserter, String details) { + //noinspection unchecked + super(event.asPrismContainerValue(), returnAsserter, details); + } + + public CaseEventAsserter assertInitiatorRef(String oid, QName typeName) { + MidPointAsserts.assertThatReferenceMatches(getEvent().getInitiatorRef(), "initiatorRef", oid, typeName); + return this; + } + + public CaseEventAsserter assertAttorneyRef(String oid, QName typeName) { + MidPointAsserts.assertThatReferenceMatches(getEvent().getAttorneyRef(), "attorneyRef", oid, typeName); + return this; + } + + public CaseEventAsserter assertOriginalAssigneeRef(String oid, QName typeName) { + CaseEventType event = getEvent(); + assertThat(event).isInstanceOf(WorkItemEventType.class); + ObjectReferenceType originalAssigneeRef = ((WorkItemEventType) event).getOriginalAssigneeRef(); + MidPointAsserts.assertThatReferenceMatches(originalAssigneeRef, "originalAssigneeRef", oid, typeName); + return this; + } + + @NotNull + private CaseEventType getEvent() { + return getPrismValue().asContainerable(); + } +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventFinder.java new file mode 100644 index 00000000000..4806d297ae6 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventFinder.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import java.util.Objects; + +import org.testng.AssertJUnit; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseEventType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemEventType; + +/** + * + */ +public class CaseEventFinder { + + private final CaseEventsAsserter eventsAsserter; + private Integer stageNumber; + private Long workItemId; + private Class type; + + public CaseEventFinder(CaseEventsAsserter eventsAsserter) { + this.eventsAsserter = eventsAsserter; + } + + public CaseEventFinder stageNumber(Integer stageNumber) { + this.stageNumber = stageNumber; + return this; + } + + public CaseEventFinder workItemId(Long workItemId) { + this.workItemId = workItemId; + return this; + } + + public CaseEventFinder type(Class type) { + this.type = type; + return this; + } + + public CaseEventAsserter> find() { + CaseEventType found = null; + for (CaseEventType event: eventsAsserter.getEvents()) { + if (matches(event)) { + if (found == null) { + found = event; + } else { + fail("Found more than one event that matches search criteria"); + } + } + } + if (found == null) { + throw new AssertionError("Found no event that matches search criteria"); + } else { + return eventsAsserter.forEvent(found); + } + } + + public CaseEventsAsserter assertNone() { + for (CaseEventType event: eventsAsserter.getEvents()) { + if (matches(event)) { + fail("Found event while not expecting it: " + event); + } + } + return eventsAsserter; + } + + public CaseEventsAsserter assertAll() { + for (CaseEventType event: eventsAsserter.getEvents()) { + if (!matches(event)) { + fail("Found event that does not match search criteria: "+event); + } + } + return eventsAsserter; + } + + private boolean matches(CaseEventType event) { + if (stageNumber != null) { + if (!Objects.equals(stageNumber, event.getStageNumber())) { + return false; + } + } + if (workItemId != null) { + if (!(event instanceof WorkItemEventType) || + !Objects.equals(workItemId, ((WorkItemEventType) event).getWorkItemId())) { + return false; + } + } + if (type != null) { + if (!type.isAssignableFrom(event.getClass())) { + return false; + } + } + return true; + } + + protected void fail(String message) { + AssertJUnit.fail(message); + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventsAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventsAsserter.java new file mode 100644 index 00000000000..f8ebed42f92 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseEventsAsserter.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseEventType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static org.testng.AssertJUnit.assertEquals; + +/** + * Asserts over a set of events. + */ +public class CaseEventsAsserter extends AbstractAsserter> { + + @NotNull private final CaseAsserter caseAsserter; + @NotNull private final List events; + + public CaseEventsAsserter(@NotNull CaseAsserter caseAsserter, @NotNull List events, String details) { + super(caseAsserter, details); + this.caseAsserter = caseAsserter; + this.events = events; + } + + PrismObject getCase() { + return caseAsserter.getObject(); + } + + public @NotNull List getEvents() { + return events; + } + + public CaseEventsAsserter assertEvents(int expected) { + assertEquals("Wrong number of events in " + desc(), expected, getEvents().size()); + return this; + } + + public CaseEventsAsserter assertNone() { + assertEvents(0); + return this; + } + + CaseEventAsserter> forEvent(CaseEventType event) { + CaseEventAsserter> asserter = new CaseEventAsserter<>(event, this, "event in "+ getCase()); + copySetupTo(asserter); + return asserter; + } + + public CaseEventAsserter> single() { + assertEvents(1); + return forEvent(getEvents().get(0)); + } + + @Override + protected String desc() { + return descWithDetails("events in "+ getCase()); + } + + public CaseEventFinder by() { + return new CaseEventFinder<>(this); + } + + public CaseEventAsserter> forStageNumber(Integer stageNumber) { + return by() + .stageNumber(stageNumber) + .find(); + } + + public CaseEventAsserter> forWorkItemId(Long workItemId) { + return by() + .workItemId(workItemId) + .find(); + } + + public CaseEventAsserter> ofType(Class type) { + return by() + .type(type) + .find(); + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseFinder.java new file mode 100644 index 00000000000..dd308bf8305 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseFinder.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import com.evolveum.midpoint.util.QNameUtil; + +import org.testng.AssertJUnit; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; + +import javax.xml.namespace.QName; + +/** + * + */ +public class CaseFinder { + + private final SubcasesAsserter subcasesAsserter; + private String targetOid; + private QName targetType; + + public CaseFinder(SubcasesAsserter subcasesAsserter) { + this.subcasesAsserter = subcasesAsserter; + } + + public CaseFinder targetOid(String targetOid) { + this.targetOid = targetOid; + return this; + } + + public CaseFinder targetType(QName targetType) { + this.targetType = targetType; + return this; + } + + public CaseAsserter> find() { + CaseType found = null; + for (CaseType subcase: subcasesAsserter.getSubcases()) { + if (matches(subcase)) { + if (found == null) { + found = subcase; + } else { + fail("Found more than one subcase that matches search criteria"); + } + } + } + if (found == null) { + throw new AssertionError("Found no subcase that matches search criteria"); + } else { + return subcasesAsserter.forSubcase(found); + } + } + + public SubcasesAsserter assertNone() { + for (CaseType subcase: subcasesAsserter.getSubcases()) { + if (matches(subcase)) { + fail("Found subcase while not expecting it: " + subcase); + } + } + return subcasesAsserter; + } + + public SubcasesAsserter assertAll() { + for (CaseType subcase: subcasesAsserter.getSubcases()) { + PrismObject assignmentTarget = null; + if (!matches(subcase)) { + fail("Found subcase that does not match search criteria: "+subcase); + } + } + return subcasesAsserter; + } + + private boolean matches(CaseType subcase) { + ObjectReferenceType targetRef = subcase.getTargetRef(); + if (targetOid != null) { + if (targetRef == null || !targetOid.equals(targetRef.getOid())) { + return false; + } + } + if (targetType != null) { + if (targetRef == null || !QNameUtil.match(targetType, targetRef.getType())) { + return false; + } + } + return true; + } + + protected void fail(String message) { + AssertJUnit.fail(message); + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemAsserter.java new file mode 100644 index 00000000000..8be42ee2a5b --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemAsserter.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.schema.constants.SchemaConstants; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.test.asserter.prism.PrismContainerValueAsserter; +import com.evolveum.midpoint.test.util.MidPointAsserts; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemOutputType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; + +/** + * Asserts about CaseWorkItemType. + */ +public class CaseWorkItemAsserter extends PrismContainerValueAsserter { + + public CaseWorkItemAsserter(CaseWorkItemType workItem) { + //noinspection unchecked + super(workItem.asPrismContainerValue()); + } + + public CaseWorkItemAsserter(CaseWorkItemType workItem, String details) { + //noinspection unchecked + super(workItem.asPrismContainerValue(), details); + } + + public CaseWorkItemAsserter(CaseWorkItemType workItem, RA returnAsserter, String details) { + //noinspection unchecked + super(workItem.asPrismContainerValue(), returnAsserter, details); + } + + @NotNull + private CaseWorkItemType getWorkItem() { + return getPrismValue().asContainerable(); + } + + public CaseWorkItemAsserter assertOriginalAssigneeRef(String oid, QName typeName) { + MidPointAsserts.assertThatReferenceMatches(getWorkItem().getOriginalAssigneeRef(), "originalAssigneeRef", oid, typeName); + return this; + } + + public CaseWorkItemAsserter assertPerformerRef(String oid, QName typeName) { + MidPointAsserts.assertThatReferenceMatches(getWorkItem().getPerformerRef(), "performerRef", oid, typeName); + return this; + } + + public CaseWorkItemAsserter assertAssignees(String... oids) { + PrismAsserts.assertReferenceValues(getWorkItem().getAssigneeRef(), oids); + return this; + } + + public CaseWorkItemAsserter assertStageNumber(Integer expected) { + assertThat(getWorkItem().getStageNumber()).as("stage number").isEqualTo(expected); + return this; + } + + public CaseWorkItemAsserter assertOutcome(String expected) { + AbstractWorkItemOutputType output = getWorkItem().getOutput(); + assertThat(output).as("output").isNotNull(); + MidPointAsserts.assertUriMatches(output.getOutcome(), "outcome", expected); + return this; + } + + public CaseWorkItemAsserter assertNameOrig(String expected) { + assertThat(getWorkItem().getName().getOrig()).as("name.orig").isEqualTo(expected); + return this; + } + + public CaseWorkItemAsserter assertRejected() { + return assertOutcome(SchemaConstants.MODEL_APPROVAL_OUTCOME_REJECT); + } + + public CaseWorkItemAsserter assertClosed() { + assertThat(getWorkItem().getCloseTimestamp()).as("closeTimestamp").isNotNull(); + return this; + } +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemFinder.java new file mode 100644 index 00000000000..a2e53f0d2a9 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemFinder.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import java.util.Objects; + +import org.testng.AssertJUnit; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; + +/** + * + */ +public class CaseWorkItemFinder { + + private final CaseWorkItemsAsserter workItemsAsserter; + private Integer stageNumber; + private Long workItemId; + + public CaseWorkItemFinder(CaseWorkItemsAsserter workItemsAsserter) { + this.workItemsAsserter = workItemsAsserter; + } + + public CaseWorkItemFinder stageNumber(Integer stageNumber) { + this.stageNumber = stageNumber; + return this; + } + + public CaseWorkItemFinder workItemId(Long workItemId) { + this.workItemId = workItemId; + return this; + } + + public CaseWorkItemAsserter> find() { + CaseWorkItemType found = null; + for (CaseWorkItemType workItem: workItemsAsserter.getWorkItems()) { + if (matches(workItem)) { + if (found == null) { + found = workItem; + } else { + fail("Found more than one workItem that matches search criteria"); + } + } + } + if (found == null) { + throw new AssertionError("Found no work item that matches search criteria"); + } else { + return workItemsAsserter.forWorkItem(found); + } + } + + public CaseWorkItemsAsserter assertNone() { + for (CaseWorkItemType workItem: workItemsAsserter.getWorkItems()) { + if (matches(workItem)) { + fail("Found workItem while not expecting it: " + workItem); + } + } + return workItemsAsserter; + } + + public CaseWorkItemsAsserter assertAll() { + for (CaseWorkItemType workItem: workItemsAsserter.getWorkItems()) { + if (!matches(workItem)) { + fail("Found work item that does not match search criteria: "+workItem); + } + } + return workItemsAsserter; + } + + private boolean matches(CaseWorkItemType workItem) { + if (stageNumber != null) { + if (!Objects.equals(stageNumber, workItem.getStageNumber())) { + return false; + } + } + if (workItemId != null) { + if (!Objects.equals(workItemId, workItem.getId())) { + return false; + } + } + return true; + } + + protected void fail(String message) { + AssertJUnit.fail(message); + } +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemsAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemsAsserter.java new file mode 100644 index 00000000000..6218bc5c2c9 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/CaseWorkItemsAsserter.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; + +/** + * Asserts over a set of work items. + */ +public class CaseWorkItemsAsserter extends AbstractAsserter> { + + @NotNull private final CaseAsserter caseAsserter; + @NotNull private final List workItems; + + public CaseWorkItemsAsserter(@NotNull CaseAsserter caseAsserter, @NotNull List workItems, String details) { + super(caseAsserter, details); + this.caseAsserter = caseAsserter; + this.workItems = workItems; + } + + PrismObject getCase() { + return caseAsserter.getObject(); + } + + public @NotNull List getWorkItems() { + return workItems; + } + + public CaseWorkItemsAsserter assertWorkItems(int expected) { + assertEquals("Wrong number of work items in " + desc(), expected, getWorkItems().size()); + return this; + } + + public CaseWorkItemsAsserter assertNone() { + assertWorkItems(0); + return this; + } + + CaseWorkItemAsserter> forWorkItem(CaseWorkItemType workItem) { + CaseWorkItemAsserter> asserter = new CaseWorkItemAsserter<>(workItem, this, "work item in "+ getCase()); + copySetupTo(asserter); + return asserter; + } + + public CaseWorkItemAsserter> single() { + assertWorkItems(1); + return forWorkItem(getWorkItems().get(0)); + } + + @Override + protected String desc() { + return descWithDetails("work items in "+ getCase()); + } + + public CaseWorkItemFinder by() { + return new CaseWorkItemFinder<>(this); + } + + public CaseWorkItemAsserter> forStageNumber(Integer stageNumber) { + return by() + .stageNumber(stageNumber) + .find(); + } + + public CaseWorkItemAsserter> forWorkItemId(Long workItemId) { + return by() + .workItemId(workItemId) + .find(); + } +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java index 6a635a4e82b..8edc4b2f121 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java @@ -337,22 +337,12 @@ public FocusAsserter assertNoItem(ItemPath itemPath) { } public FocusAsserter assertArchetypeRef(String expectedArchetypeOid) { - List archetypeRefs = getObject().asObjectable().getArchetypeRef(); - if (archetypeRefs == null || archetypeRefs.isEmpty()) { - fail("No archetypeRefs while archetype "+expectedArchetypeOid+" expected"); - } - if (archetypeRefs.size() > 1) { - fail("Too many archetypes while archetypeRefs "+expectedArchetypeOid+" expected: "+archetypeRefs); - } - assertEquals("Wrong archetypeRef in "+desc(), expectedArchetypeOid, archetypeRefs.get(0).getOid()); + super.assertArchetypeRef(expectedArchetypeOid); return this; } public FocusAsserter assertNoArchetypeRef() { - List archetypeRefs = getObject().asObjectable().getArchetypeRef(); - if (archetypeRefs != null && !archetypeRefs.isEmpty()) { - fail("Found archetypeRefs while not expected any: "+archetypeRefs); - } + super.assertNoArchetypeRef(); return this; } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/SubcasesAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/SubcasesAsserter.java new file mode 100644 index 00000000000..a7829d23fa1 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/SubcasesAsserter.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 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.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; + +import java.util.List; +import javax.xml.namespace.QName; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; + +/** + * Asserts over a set of subcases. + * + */ +public class SubcasesAsserter extends AbstractAsserter { + + @NotNull private final CaseAsserter parentCaseAsserter; + @NotNull private final List subcases; + + public SubcasesAsserter(@NotNull CaseAsserter parentCaseAsserter, @NotNull List subcases, String details) { + super(details); + this.parentCaseAsserter = parentCaseAsserter; + this.subcases = subcases; + } + + PrismObject getParentCase() { + return parentCaseAsserter.getObject(); + } + + public @NotNull List getSubcases() { + return subcases; + } + + public SubcasesAsserter assertSubcases(int expected) { + assertEquals("Wrong number of subcases in " + desc(), expected, getSubcases().size()); + return this; + } + + public SubcasesAsserter assertNone() { + assertSubcases(0); + return this; + } + + CaseAsserter> forSubcase(CaseType subcase) { + CaseAsserter> asserter = new CaseAsserter<>(subcase.asPrismObject(), this, "subcase of "+getParentCase()); + copySetupTo(asserter); + return asserter; + } + + public CaseAsserter> single() { + assertSubcases(1); + return forSubcase(getSubcases().get(0)); + } + + @Override + protected String desc() { + return descWithDetails("subcases of "+ getParentCase()); + } + + public CaseFinder by() { + return new CaseFinder<>(this); + } + + public CaseAsserter> forTarget(String targetOid) { + return by() + .targetOid(targetOid) + .find(); + } + + public CaseAsserter> forTarget(String targetOid, QName targetType) { + return by() + .targetOid(targetOid) + .targetType(targetType) + .find(); + } +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/prism/PrismObjectAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/prism/PrismObjectAsserter.java index 3b5567846f2..189dc70a9fe 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/prism/PrismObjectAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/prism/PrismObjectAsserter.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.test.asserter.prism; +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; @@ -20,6 +22,10 @@ import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.test.asserter.*; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + import org.jetbrains.annotations.NotNull; import org.testng.AssertJUnit; @@ -29,21 +35,8 @@ import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.test.IntegrationTestTools; -import com.evolveum.midpoint.test.asserter.AbstractAsserter; -import com.evolveum.midpoint.test.asserter.ExtensionAsserter; -import com.evolveum.midpoint.test.asserter.OrgAsserter; -import com.evolveum.midpoint.test.asserter.ParentOrgRefsAsserter; -import com.evolveum.midpoint.test.asserter.RoleAsserter; -import com.evolveum.midpoint.test.asserter.TriggersAsserter; -import com.evolveum.midpoint.test.asserter.UserAsserter; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** * @author semancik @@ -75,6 +68,10 @@ public PrismObject getObject() { return object; } + public O getObjectable() { + return object.asObjectable(); + } + public static PrismObjectAsserter forObject(PrismObject shadow) { return new PrismObjectAsserter<>(shadow); } @@ -126,6 +123,13 @@ public PrismObjectAsserter assertName(String expectedOrig) { return this; } + public PrismObjectAsserter assertNameOrig(String expectedOrig) { + assertThat(getObject().getName().getOrig()) + .as("Name (orig) in " + desc()) + .isEqualTo(expectedOrig); + return this; + } + public PolyStringAsserter> name() { PolyStringAsserter> asserter = new PolyStringAsserter<>(getPolyStringPropertyValue(ObjectType.F_NAME), this, "name in "+desc()); copySetupTo(asserter); @@ -348,4 +352,31 @@ public PrismObject getCachedObject(Class type, S copySetupTo(asserter); return asserter; } + + public PrismObjectAsserter assertArchetypeRef(String expectedArchetypeOid) { + List archetypeRefs = getArchetypeRefs(); + if (archetypeRefs.isEmpty()) { + fail("No archetypeRefs while archetype "+expectedArchetypeOid+" expected"); + } + if (archetypeRefs.size() > 1) { + fail("Too many archetypes while archetypeRefs "+expectedArchetypeOid+" expected: "+archetypeRefs); + } + assertEquals("Wrong archetypeRef in "+desc(), expectedArchetypeOid, archetypeRefs.get(0).getOid()); + return this; + } + + @NotNull + private List getArchetypeRefs() { + O objectable = getObject().asObjectable(); + return objectable instanceof AssignmentHolderType ? + ((AssignmentHolderType) objectable).getArchetypeRef() : emptyList(); + } + + public PrismObjectAsserter assertNoArchetypeRef() { + List archetypeRefs = getArchetypeRefs(); + if (!archetypeRefs.isEmpty()) { + fail("Found archetypeRefs while not expected any: "+archetypeRefs); + } + return this; + } } diff --git a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java index dd32c273bcc..3dc0bf79554 100644 --- a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java +++ b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java @@ -26,11 +26,7 @@ import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OrderConstraintsType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** * @author Radovan Semancik @@ -86,7 +82,7 @@ void authorize(String operationUrl, /** * Returns a filter that applies to all the objects/targets for which the principal is authorized. * - * E.g. it can return a filter of all assignable roles for a principlal. In that case #assign authorization is used, + * E.g. it can return a filter of all assignable roles for a principal. In that case #assign authorization is used, * and object is the user which should hold the assignment. * * If it returns NoneFilter then no search should be done. The principal is not authorized for this operation at all. @@ -128,7 +124,7 @@ F computeSecurityFilter(MidPoint ItemSecurityConstraints getAllowedRequestAssignmentItems(MidPointPrincipal midPointPrincipal, String operationUrl, PrismObject object, PrismObject target, OwnerResolver ownerResolver, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; - MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, String attorneyAuthorizationAction, PrismObject donor, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; + MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, String attorneyAuthorizationAction, PrismObject donor, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; AccessDecision determineSubitemDecision(ObjectSecurityConstraints securityConstraints, ObjectDelta delta, PrismObject currentObject, String operationUrl, AuthorizationPhaseType phase, ItemPath subitemRootPath); diff --git a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java index 6defad9ee29..a4a86d11c05 100644 --- a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java +++ b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java @@ -2174,13 +2174,13 @@ public ItemSecurityConstraint } @Override - public MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, String attorneyAuthorizationAction, PrismObject donor, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + public MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipal, String attorneyAuthorizationAction, PrismObject donor, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { if (attorneyPrincipal.getAttorney() != null) { throw new UnsupportedOperationException("Transitive attorney is not supported yet"); } AuthorizationLimitationsCollector limitationsCollector = new AuthorizationLimitationsCollector(); - AuthorizationParameters autzParams = AuthorizationParameters.Builder.buildObject(donor); + AuthorizationParameters autzParams = AuthorizationParameters.Builder.buildObject(donor); AccessDecision decision = isAuthorizedInternal(attorneyPrincipal, attorneyAuthorizationAction, null, autzParams, null, limitationsCollector, task, result); if (!decision.equals(AccessDecision.ALLOW)) { failAuthorization(attorneyAuthorizationAction, null, autzParams, result);