/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.wf.impl.policy.assignments; import com.evolveum.midpoint.model.api.context.ModelState; import com.evolveum.midpoint.model.api.util.DeputyUtils; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; 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.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.impl.policy.AbstractWfTestPolicy; import com.evolveum.midpoint.wf.impl.policy.ExpectedTask; import com.evolveum.midpoint.wf.impl.policy.ExpectedWorkItem; import com.evolveum.midpoint.wf.impl.WorkflowResult; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Test; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.createAssignmentTo; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertEquals; /** * Testing approvals of role assignments: create/delete assignment, potentially for more roles and combined with other operations. * Testing also with deputies specified. * * Subclasses provide specializations regarding ways how rules and/or approvers are attached to roles. * * @author mederly */ @ContextConfiguration(locations = {"classpath:ctx-workflow-test-main.xml"}) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public abstract class AbstractTestAssignmentApproval extends AbstractWfTestPolicy { protected static final Trace LOGGER = TraceManager.getTrace(AbstractTestAssignmentApproval.class); protected abstract String getRoleOid(int number); protected abstract String getRoleName(int number); /** * The simplest case: addition of an assignment of single security-sensitive role (Role1). * Although it induces Role10 membership, it is not a problem, as Role10 approver (Lead10) is not imported yet. * (Even if it was, Role10 assignment would not be approved, see test030.) */ @Test public void test010AddRole1Assignment() throws Exception { final String TEST_NAME = "test010AddRole1Assignment"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); executeAssignRole1ToJack(TEST_NAME, false, false, null, null); } /** * Removing recently added assignment of single security-sensitive role. Should execute without approval (for now). */ @Test public void test020DeleteRole1Assignment() throws Exception { final String TEST_NAME = "test020DeleteRole1Assignment"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); LensContext<UserType> context = createUserLensContext(); fillContextWithUser(context, userJackOid, result); addFocusDeltaToContext(context, (ObjectDelta<UserType>) DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).delete(createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid)); clockwork.run(context, task, result); assertEquals("Wrong context state", ModelState.FINAL, context.getState()); TestUtil.assertSuccess(result); assertNotAssignedRole(getUser(userJackOid), getRoleOid(1), task, result); } /** * Repeating test010; this time with Lead10 present. So we are approving an assignment of single security-sensitive role (Role1), * that induces another security-sensitive role (Role10). Because of current implementation constraints, only the first assignment * should be brought to approval. */ @Test public void test030AddRole1AssignmentAgain() throws Exception { final String TEST_NAME = "test030AddRole1AssignmentAgain"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); Task task = createTask(TEST_NAME); importLead10(task, task.getResult()); executeAssignRole1ToJack(TEST_NAME, false, false, null, null); } /** * The same as above, but with immediate execution. */ @Test public void test040AddRole1AssignmentImmediate() throws Exception { final String TEST_NAME = "test040AddRole1AssignmentImmediate"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRole1ToJack(TEST_NAME, true, false, null, null); } /** * Attempt to assign roles 1, 2, 3, 4 along with changing description. Assignment of role 4 and description change * are not to be approved. * * Decisions for roles 1-3 are rejected. */ @Test public void test050AddRoles123AssignmentNNN() throws Exception { final String TEST_NAME = "test050AddRoles123AssignmentNNN"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, false, false, false, false); } /** * The same as above, but with immediate execution. */ @Test public void test052AddRoles123AssignmentNNNImmediate() throws Exception { final String TEST_NAME = "test052AddRoles123AssignmentNNNImmediate"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, true, false, false, false); } /** * Attempt to assign roles 1, 2, 3, 4 along with changing description. Assignment of role 4 and description change * are not to be approved. * * Decision for role 1 is accepted. */ @Test public void test060AddRoles123AssignmentYNN() throws Exception { final String TEST_NAME = "test060AddRoles123AssignmentYNN"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, false, true, false, false); } @Test public void test062AddRoles123AssignmentYNNImmediate() throws Exception { final String TEST_NAME = "test062AddRoles123AssignmentYNNImmediate"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, true, true, false, false); } /** * Attempt to assign roles 1, 2, 3, 4 along with changing description. Assignment of role 4 and description change * are not to be approved. * * Decisions for roles 1-3 are accepted. */ @Test public void test070AddRoles123AssignmentYYY() throws Exception { final String TEST_NAME = "test070AddRoles123AssignmentYYY"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, false, true, true, true); } @Test public void test072AddRoles123AssignmentYYYImmediate() throws Exception { final String TEST_NAME = "test072AddRoles123AssignmentYYYImmediate"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRoles123ToJack(TEST_NAME, true, true, true, true); } /** * Assigning Role1 with two deputies present. (But approved by the delegator.) */ @Test public void test130AddRole1aAssignmentWithDeputy() throws Exception { final String TEST_NAME = "test130AddRole1aAssignmentWithDeputy"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); Task task = createTask(TEST_NAME); importLead1Deputies(task, task.getResult()); unassignAllRoles(userJackOid); executeAssignRole1ToJack(TEST_NAME, false, true, null, null); } /** * Assigning Role1 with two deputies present. (Approved by one of the deputies.) */ @Test public void test132AddRole1aAssignmentWithDeputyApprovedByDeputy1() throws Exception { final String TEST_NAME = "test132AddRole1aAssignmentWithDeputyApprovedByDeputy1"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRole1ToJack(TEST_NAME, false, true, userLead1Deputy1Oid, null); } @Test(enabled = false) public void test150AddRole1ApproverAssignment() throws Exception { final String TEST_NAME = "test150AddRole1ApproverAssignment"; TestUtil.displayTestTile(this, TEST_NAME); login(userAdministrator); unassignAllRoles(userJackOid); executeAssignRole1ToJack(TEST_NAME, false, true, null, SchemaConstants.ORG_APPROVER); } private void executeAssignRole1ToJack(String TEST_NAME, boolean immediate, boolean deputy, String approverOid, QName relation) throws Exception { PrismObject<UserType> jack = getUser(userJackOid); AssignmentType assignment = createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext); assignment.getTargetRef().setRelation(relation); ObjectDelta<UserType> addRole1Delta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(assignment) .asObjectDelta(userJackOid); String realApproverOid = approverOid != null ? approverOid : userLead1Oid; executeTest2(TEST_NAME, new TestDetails2<UserType>() { @Override protected PrismObject<UserType> getFocus(OperationResult result) throws Exception { return jack.clone(); } @Override protected ObjectDelta<UserType> getFocusDelta() throws SchemaException { return addRole1Delta.clone(); } @Override protected int getNumberOfDeltasToApprove() { return 1; } @Override protected List<Boolean> getApprovals() { return Collections.singletonList(true); } @Override protected List<ObjectDelta<UserType>> getExpectedDeltasToApprove() { return Collections.singletonList(addRole1Delta.clone()); } @Override protected ObjectDelta<UserType> getExpectedDelta0() { return ObjectDelta.createModifyDelta(jack.getOid(), Collections.emptyList(), UserType.class, prismContext); } @Override protected String getObjectOid() { return jack.getOid(); } @Override protected List<ExpectedTask> getExpectedTasks() { return Collections.singletonList(new ExpectedTask(getRoleOid(1), "Assigning " + getRoleName(1) + " to jack")); } @Override protected List<ExpectedWorkItem> getExpectedWorkItems() { ExpectedTask etask = getExpectedTasks().get(0); return Collections.singletonList(new ExpectedWorkItem(userLead1Oid, getRoleOid(1), etask)); } @Override protected void assertDeltaExecuted(int number, boolean yes, Task rootTask, OperationResult result) throws Exception { if (number == 1) { if (yes) { assertAssignedRole(userJackOid, getRoleOid(1), rootTask, result); checkWorkItemAuditRecords(createResultMap(getRoleOid(1), WorkflowResult.APPROVED)); checkUserApprovers(userJackOid, Collections.singletonList(realApproverOid), result); } else { assertNotAssignedRole(userJackOid, getRoleOid(1), rootTask, result); } } } @Override protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task) throws Exception { assertActiveWorkItems(userLead1Oid, 1); assertActiveWorkItems(userLead1Deputy1Oid, deputy ? 1 : 0); assertActiveWorkItems(userLead1Deputy2Oid, deputy ? 1 : 0); checkTargetOid(executionId, getRoleOid(1)); login(getUser(realApproverOid)); return true; } }, 1, immediate); } protected List<PrismReferenceValue> getPotentialAssignees(PrismObject<UserType> user) { List<PrismReferenceValue> rv = new ArrayList<>(); rv.add(ObjectTypeUtil.createObjectRef(user).asReferenceValue()); rv.addAll(DeputyUtils.getDelegatorReferences(user.asObjectable())); return rv; } protected void assertActiveWorkItems(String approverOid, int expectedCount) throws Exception { if (approverOid == null && expectedCount == 0) { return; } Task task = createTask("query"); ObjectQuery query = QueryBuilder.queryFor(WorkItemType.class, prismContext) .item(WorkItemType.F_ASSIGNEE_REF).ref(getPotentialAssignees(getUser(approverOid))) .build(); List<WorkItemType> items = modelService.searchContainers(WorkItemType.class, query, null, task, task.getResult()); assertEquals("Wrong active work items for " + approverOid, expectedCount, items.size()); } private void executeAssignRoles123ToJack(String TEST_NAME, boolean immediate, boolean approve1, boolean approve2, boolean approve3) throws Exception { PrismObject<UserType> jack = getUser(userJackOid); @SuppressWarnings("unchecked") ObjectDelta<UserType> addRole1Delta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") ObjectDelta<UserType> addRole2Delta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(2), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") ObjectDelta<UserType> addRole3Delta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(3), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") ObjectDelta<UserType> addRole4Delta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(4), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") ObjectDelta<UserType> changeDescriptionDelta = (ObjectDelta<UserType>) DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace(TEST_NAME) .asObjectDelta(userJackOid); ObjectDelta<UserType> primaryDelta = ObjectDelta.summarize(addRole1Delta, addRole2Delta, addRole3Delta, addRole4Delta, changeDescriptionDelta); ObjectDelta<UserType> delta0 = ObjectDelta.summarize(addRole4Delta, changeDescriptionDelta); String originalDescription = getUser(userJackOid).asObjectable().getDescription(); executeTest2(TEST_NAME, new TestDetails2<UserType>() { @Override protected PrismObject<UserType> getFocus(OperationResult result) throws Exception { return jack.clone(); } @Override protected ObjectDelta<UserType> getFocusDelta() throws SchemaException { return primaryDelta.clone(); } @Override protected int getNumberOfDeltasToApprove() { return 3; } @Override protected List<Boolean> getApprovals() { return Arrays.asList(approve1, approve2, approve3); } @Override protected List<ObjectDelta<UserType>> getExpectedDeltasToApprove() { return Arrays.asList(addRole1Delta.clone(), addRole2Delta.clone(), addRole3Delta.clone()); } @Override protected ObjectDelta<UserType> getExpectedDelta0() { return delta0.clone(); } @Override protected String getObjectOid() { return jack.getOid(); } @Override protected List<ExpectedTask> getExpectedTasks() { return Arrays.asList( new ExpectedTask(getRoleOid(1), "Assigning "+getRoleName(1)+" to jack"), new ExpectedTask(getRoleOid(2), "Assigning "+getRoleName(2)+" to jack"), new ExpectedTask(getRoleOid(3), "Assigning "+getRoleName(3)+" to jack")); } @Override protected List<ExpectedWorkItem> getExpectedWorkItems() { List<ExpectedTask> etasks = getExpectedTasks(); return Arrays.asList( new ExpectedWorkItem(userLead1Oid, getRoleOid(1), etasks.get(0)), new ExpectedWorkItem(userLead2Oid, getRoleOid(2), etasks.get(1)), new ExpectedWorkItem(userLead3Oid, getRoleOid(3), etasks.get(2)) ); } @Override protected void assertDeltaExecuted(int number, boolean yes, Task rootTask, OperationResult result) throws Exception { switch (number) { case 0: if (yes) { assertUserProperty(userJackOid, UserType.F_DESCRIPTION, TEST_NAME); assertAssignedRole(userJackOid, getRoleOid(4), rootTask, result); } else { if (originalDescription != null) { assertUserProperty(userJackOid, UserType.F_DESCRIPTION, originalDescription); } else { assertUserNoProperty(userJackOid, UserType.F_DESCRIPTION); } assertNotAssignedRole(userJackOid, getRoleOid(4), rootTask, result); } break; case 1: case 2: case 3: if (yes) { assertAssignedRole(userJackOid, getRoleOid(number), rootTask, result); } else { assertNotAssignedRole(userJackOid, getRoleOid(number), rootTask, result); } break; } } @Override protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task) throws Exception { String targetOid = getTargetOid(executionId); if (getRoleOid(1).equals(targetOid)) { login(getUser(userLead1Oid)); return approve1; } else if (getRoleOid(2).equals(targetOid)) { login(getUser(userLead2Oid)); return approve2; } else if (getRoleOid(3).equals(targetOid)) { login(getUser(userLead3Oid)); return approve3; } else { throw new IllegalStateException("Unexpected approval request for " + targetOid); } } }, 3, immediate); } @Test public void zzzMarkAsNotInitialized() { display("Setting class as not initialized"); unsetSystemInitialized(); } }