1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.actions;
17
18 import junit.framework.Assert;
19 import org.apache.commons.lang.ArrayUtils;
20 import org.junit.Test;
21 import org.kuali.rice.coreservice.api.parameter.Parameter;
22 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
23 import org.kuali.rice.kew.actionitem.ActionItem;
24 import org.kuali.rice.kew.api.KewApiConstants;
25 import org.kuali.rice.kew.api.KewApiServiceLocator;
26 import org.kuali.rice.kew.api.WorkflowDocument;
27 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
28 import org.kuali.rice.kew.api.action.ActionRequest;
29 import org.kuali.rice.kew.api.action.ActionRequestType;
30 import org.kuali.rice.kew.api.action.ActionType;
31 import org.kuali.rice.kew.api.action.InvalidActionTakenException;
32 import org.kuali.rice.kew.doctype.bo.DocumentType;
33 import org.kuali.rice.kew.doctype.service.impl.KimDocumentTypeAuthorizer;
34 import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
35 import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
36 import org.kuali.rice.kew.postprocessor.DefaultPostProcessor;
37 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
38 import org.kuali.rice.kew.service.KEWServiceLocator;
39 import org.kuali.rice.kew.test.KEWTestCase;
40 import org.kuali.rice.kim.api.KimConstants;
41 import org.kuali.rice.kim.api.common.attribute.KimAttribute;
42 import org.kuali.rice.kim.api.common.template.Template;
43 import org.kuali.rice.kim.api.permission.Permission;
44 import org.kuali.rice.kim.api.role.Role;
45 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
46 import org.kuali.rice.kim.api.type.KimType;
47 import org.kuali.rice.kim.api.type.KimTypeAttribute;
48 import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
49 import org.kuali.rice.kim.impl.type.KimTypeBo;
50 import org.kuali.rice.krad.service.KRADServiceLocator;
51 import org.kuali.rice.krad.util.KRADConstants;
52 import org.kuali.rice.test.BaselineTestCase;
53
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59
60 import static org.junit.Assert.*;
61
62 public class RecallActionTest extends KEWTestCase {
63
64
65
66 public static class RecallTestPostProcessor extends DefaultPostProcessor {
67 public static ActionType afterActionTakenType;
68 public static ActionTakenEvent afterActionTakenEvent;
69 @Override
70 public ProcessDocReport afterActionTaken(ActionType performed, ActionTakenEvent event) throws Exception {
71 afterActionTakenType = performed;
72 afterActionTakenEvent = event;
73 return super.afterActionTaken(performed, event);
74 }
75 }
76
77 public static class RecallTestDocumentTypeAuthorizer extends KimDocumentTypeAuthorizer {
78 public static String CUSTOM_RECALL_KIM_TYPE_NAME = "Dynamic Type";
79 public static String CUSTOM_RECALL_QUALIFIER_NAME = "Dynamic Qualifier";
80 public static String CUSTOM_RECALL_QUALIFIER_VALUE = "Dynamic Qualifier Value";
81
82 public static String CUSTOM_RECALL_DETAIL_NAME = KimConstants.AttributeConstants.APP_DOC_STATUS;
83 public static String CUSTOM_RECALL_DETAIL_VALUE = "Dynamic Recall Permission Detail Value";
84
85 public static boolean buildPermissionDetailsInvoked = false;
86 public static boolean buildRoleQualifiersInvoked= false;
87
88 @Override
89 protected Map<String, String> buildDocumentTypePermissionDetails(DocumentType documentType, String documentStatus, String actionRequestedCode, String routeNodeName) {
90 buildPermissionDetailsInvoked = true;
91 Map<String, String> details = super.buildDocumentTypePermissionDetails(documentType, documentStatus, actionRequestedCode, routeNodeName);
92 details.put(CUSTOM_RECALL_DETAIL_NAME, CUSTOM_RECALL_DETAIL_VALUE);
93 return details;
94 }
95
96 @Override
97 protected Map<String, String> buildDocumentRoleQualifiers(DocumentRouteHeaderValue document, String routeNodeName) {
98 buildRoleQualifiersInvoked = true;
99 Map<String, String> qualifiers = super.buildDocumentRoleQualifiers(document, routeNodeName);
100 qualifiers.put(CUSTOM_RECALL_QUALIFIER_NAME, CUSTOM_RECALL_QUALIFIER_VALUE);
101 return qualifiers;
102 }
103 }
104
105 private static final String RECALL_TEST_DOC = "RecallTest";
106 private static final String RECALL_TEST_RESTRICTED_DOC = "RecallTestRestricted";
107 private static final String RECALL_TEST_NOROUTING_DOC = "RecallTestNoRouting";
108 private static final String RECALL_TEST_ONLYADHOC_DOC = "RecallTestOnlyAdhoc";
109 private static final String RECALL_NOTIFY_TEST_DOC = "RecallWithPrevNotifyTest";
110 private static final String RECALL_NO_PENDING_NOTIFY_TEST_DOC = "RecallWithoutPendingNotifyTest";
111 private static final String RECALL_NOTIFY_THIRDPARTY_TEST_DOC = "RecallWithThirdPartyNotifyTest";
112
113 private String EWESTFAL = null;
114 private String JHOPF = null;
115 private String RKIRKEND = null;
116 private String NATJOHNS = null;
117 private String BMCGOUGH = null;
118
119 protected void loadTestData() throws Exception {
120 loadXmlFile("ActionsConfig.xml");
121 }
122
123 @Override
124 protected void setUpAfterDataLoad() throws Exception {
125 super.setUpAfterDataLoad();
126 EWESTFAL = getPrincipalIdForName("ewestfal");
127 JHOPF = getPrincipalIdForName("jhopf");
128 RKIRKEND = getPrincipalIdForName("rkirkend");
129 NATJOHNS = getPrincipalIdForName("natjohns");
130 BMCGOUGH = getPrincipalIdForName("bmcgough");
131
132 RecallTestPostProcessor.afterActionTakenType = null;
133 RecallTestPostProcessor.afterActionTakenEvent = null;
134 }
135
136 protected void assertAfterActionTakenCalled(ActionType performed, ActionType taken) {
137 assertEquals(performed, RecallTestPostProcessor.afterActionTakenType);
138 assertNotNull(RecallTestPostProcessor.afterActionTakenEvent);
139 assertEquals(taken, RecallTestPostProcessor.afterActionTakenEvent.getActionTaken().getActionTaken());
140 }
141
142 @Test(expected=InvalidActionTakenException.class) public void testCantRecallUnroutedDoc() {
143 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
144 document.recall("recalling", true);
145 }
146
147 @Test public void testRecallAsInitiatorBeforeAnyApprovals() throws Exception {
148 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
149 document.route("");
150
151 document.recall("recalling", true);
152
153 assertTrue("Document should be recalled", document.isRecalled());
154 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.RECALL);
155
156
157
158 List requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
159 assertEquals("Should not have any active requests", 0, requests.size());
160
161 Collection<ActionItem> actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId());
162 assertEquals("Should not have any action items", 0, actionItems.size());
163 }
164
165 @Test public void testRecallValidActionsTaken() throws Exception {
166
167 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_RESTRICTED_DOC);
168 document.route("routing");
169 document.recall("recalling", true);
170
171
172 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_RESTRICTED_DOC);
173 document.saveDocument("saving");
174 document.route("routing");
175 document.recall("recalling", true);
176 }
177
178 @Test
179 public void testRecallInvalidActionsTaken() throws Exception {
180 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_RESTRICTED_DOC);
181 document.route("");
182
183 document = WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId());
184 document.approve("");
185
186 try {
187 document.recall("recalling", true);
188 fail("Recall should NOT have succeeded. Expected InvalidActionTakenException due to invalid 'APROVE' prior action taken.");
189 } catch (InvalidActionTakenException iate) {
190 assertTrue(iate.getMessage().contains("Invalid prior action taken: 'APPROVE'"));
191 }
192 }
193
194 @Test public void testRecallOnlyAdhocRouting() throws Exception {
195 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_ONLYADHOC_DOC);
196
197 document.adHocToPrincipal(ActionRequestType.APPROVE, "adhoc approve to JHOPF", JHOPF, "adhocing to prevent finalization", true);
198 document.route("routing");
199 try {
200 document.recall("recalling", true);
201 fail("Recall should NOT have succeeded. Expected InvalidActionTakenException due to absence of non-adhoc route nodes.");
202 } catch (InvalidActionTakenException iate) {
203 assertTrue(iate.getMessage().contains("No non-adhoc route nodes defined"));
204 }
205 }
206
207 @Test public void testRecallAsInitiatorAfterSingleApproval() throws Exception {
208 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
209 document.route("");
210
211 document = WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId());
212 document.approve("");
213
214 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId());
215 document.recall("recalling", true);
216
217 assertTrue("Document should be recalled", document.isRecalled());
218 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.RECALL);
219
220
221
222 List requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
223 assertEquals("Should not have any active requests", 0, requests.size());
224
225 Collection<ActionItem> actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId());
226 assertEquals("Should not have any action items", 0, actionItems.size());
227
228
229 assertFalse(document.getValidActions().getValidActions().contains(ActionType.RECALL));
230 }
231
232 @Test(expected=InvalidActionTakenException.class)
233 public void testRecallInvalidWhenProcessed() throws Exception {
234 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
235 document.route("");
236
237 for (String user: new String[] { JHOPF, EWESTFAL, RKIRKEND, NATJOHNS, BMCGOUGH }) {
238 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId());
239 document.approve("");
240 }
241
242 document.refresh();
243 assertTrue("Document should be processed", document.isProcessed());
244 assertTrue("Document should be approved", document.isApproved());
245 assertFalse("Document should not be final", document.isFinal());
246
247 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId());
248 document.recall("recalling when processed should fail", true);
249 }
250
251 @Test(expected=InvalidActionTakenException.class)
252 public void testRecallInvalidWhenFinal() throws Exception {
253 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
254 document.route("");
255
256 for (String user: new String[] { JHOPF, EWESTFAL, RKIRKEND, NATJOHNS, BMCGOUGH }) {
257 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId());
258 document.approve("");
259 }
260 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
261 document.acknowledge("");
262
263 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("jthomas"), document.getDocumentId());
264 document.fyi();
265
266 for (ActionRequest a: document.getRootActionRequests()) {
267 System.err.println(a);
268 if (a.isAcknowledgeRequest() || a.isFyiRequest()) {
269 System.err.println(a.getPrincipalId());
270 System.err.println(KimApiServiceLocator.getIdentityService().getPrincipal(a.getPrincipalId()).getPrincipalName());
271 }
272 }
273
274 assertFalse("Document should not be processed", document.isProcessed());
275 assertTrue("Document should be approved", document.isApproved());
276 assertTrue("Document should be final", document.isFinal());
277
278 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId());
279 document.recall("recalling when processed should fail", true);
280 }
281
282 @Test public void testRecallToActionListAsInitiatorBeforeAnyApprovals() throws Exception {
283 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
284 document.route("");
285
286 document.recall("recalling", false);
287
288 assertTrue("Document should be saved", document.isSaved());
289 assertEquals(1, document.getCurrentNodeNames().size());
290 assertTrue(document.getCurrentNodeNames().contains("AdHoc"));
291 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.COMPLETE);
292
293
294 assertTrue(document.isCompletionRequested());
295
296 assertFalse(document.getValidActions().getValidActions().contains(ActionType.RECALL));
297
298
299 assertTrue(WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isFYIRequested());
300
301 document.complete("completing");
302
303 assertTrue("Document should be enroute", document.isEnroute());
304
305 assertTrue(WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isApprovalRequested());
306 }
307
308 private static final String PERM_APP_DOC_STATUS = "recallable by admins";
309 private static final String ROUTE_NODE = "NotifyFirst";
310 private static final String ROUTE_STATUS = "R";
311
312 protected Permission createRecallPermission(String docType, String appDocStatus, String routeNode, String routeStatus) {
313 return createPermissionForTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION + " for test case", docType, appDocStatus, routeNode, routeStatus);
314 }
315
316 protected Permission createRouteDocumentPermission(String docType, String appDocStatus, String routeNode, String routeStatus) {
317 return createPermissionForTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.ROUTE_PERMISSION, KewApiConstants.KEW_NAMESPACE, KewApiConstants.ROUTE_PERMISSION + " for test case", docType, appDocStatus, routeNode, routeStatus);
318 }
319
320 protected Permission createPermissionForTemplate(String template_ns, String template_name, String permission_ns, String permission_name, String docType, String appDocStatus, String routeNode, String routeStatus) {
321 Template permTmpl = KimApiServiceLocator.getPermissionService().findPermTemplateByNamespaceCodeAndName(template_ns, template_name);
322 assertNotNull(permTmpl);
323 Permission.Builder permission = Permission.Builder.create(permission_ns, permission_name);
324 permission.setDescription(permission_name);
325 permission.setTemplate(Template.Builder.create(permTmpl));
326 Map<String, String> attrs = new HashMap<String, String>();
327 attrs.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, docType);
328 attrs.put(KimConstants.AttributeConstants.APP_DOC_STATUS, appDocStatus);
329 attrs.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, routeNode);
330 attrs.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, routeStatus);
331 permission.setActive(true);
332 permission.setAttributes(attrs);
333
334
335 Permission perm = KimApiServiceLocator.getPermissionService().createPermission(permission.build());
336 assertEquals(perm.getTemplate().getId(), permTmpl.getId());
337 int num = 1;
338 if (appDocStatus != null) num++;
339 if (routeNode != null) num++;
340 if (routeStatus != null) num++;
341 assertEquals(num, perm.getAttributes().size());
342 assertEquals(docType, perm.getAttributes().get(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME));
343 assertEquals(appDocStatus, perm.getAttributes().get(KimConstants.AttributeConstants.APP_DOC_STATUS));
344 assertEquals(routeNode, perm.getAttributes().get(KimConstants.AttributeConstants.ROUTE_NODE_NAME));
345 assertEquals(routeStatus, perm.getAttributes().get(KimConstants.AttributeConstants.ROUTE_STATUS_CODE));
346
347 return perm;
348 }
349
350
351 protected void disableInitiatorRecallPermission() {
352 Permission p = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName("KR-WKFLW", "Recall Document");
353 Permission.Builder pb = Permission.Builder.create(p);
354 pb.setActive(false);
355 KimApiServiceLocator.getPermissionService().updatePermission(pb.build());
356 }
357
358
359 protected void setKimPriorityOnDocumentTypeParameterValue(String parameterValue) {
360 if(CoreFrameworkServiceLocator.getParameterService().parameterExists(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND)) {
361 Parameter kimPriorityOverDocTypePolicyParameter = CoreFrameworkServiceLocator.getParameterService().getParameter(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND);
362 Parameter.Builder b = Parameter.Builder.create(kimPriorityOverDocTypePolicyParameter);
363 b.setValue(parameterValue);
364 CoreFrameworkServiceLocator.getParameterService().updateParameter(b.build());
365 }
366 }
367
368 protected String getKimPriorityOnDocumentTypeParameterValue() {
369 if(CoreFrameworkServiceLocator.getParameterService().parameterExists(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND)) {
370 return CoreFrameworkServiceLocator.getParameterService().getParameter(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND).getValue();
371 }
372 return null;
373 }
374
375
376
377
378
379
380 @Test public void testRecallPermissionMatching() {
381 disableInitiatorRecallPermission();
382 createRecallPermission(RECALL_TEST_DOC, PERM_APP_DOC_STATUS, ROUTE_NODE, ROUTE_STATUS);
383
384 Map<String, String> details = new HashMap<String, String>();
385 details.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, RECALL_TEST_DOC);
386 details.put(KimConstants.AttributeConstants.APP_DOC_STATUS, PERM_APP_DOC_STATUS);
387 details.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, ROUTE_NODE);
388 details.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, ROUTE_STATUS);
389
390
391 for (Map.Entry<String, String> entry: details.entrySet()) {
392 Map<String, String> testDetails = new HashMap<String, String>(details);
393
394 testDetails.put(entry.getKey(), entry.getValue() + " BOGUS ");
395 assertFalse("non-matching " + entry.getKey() + " detail should cause template to not match", KimApiServiceLocator.getPermissionService().isPermissionDefinedByTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, testDetails));
396 }
397
398 assertTrue("template should match details", KimApiServiceLocator.getPermissionService().isPermissionDefinedByTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, details));
399 }
400
401 @Test public void testRecallPermissionTemplate() throws Exception {
402 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
403 document.route("");
404
405
406 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
407 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
408
409
410 Permission perm = createRecallPermission(RECALL_TEST_DOC, PERM_APP_DOC_STATUS, ROUTE_NODE, ROUTE_STATUS);
411
412
413 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator");
414 KimApiServiceLocator.getRoleService().assignPermissionToRole(perm.getId(), techadmin.getId());
415
416
417
418
419 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_NOTIFY_TEST_DOC);
420 document.route(PERM_APP_DOC_STATUS);
421 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
422 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
423
424
425 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
426 document.route("");
427
428 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
429 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
430
431
432 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
433 document.route("");
434 WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).approve("");
435 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
436 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
437
438
439 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
440 document.route("");
441 document.cancel("cancelled");
442 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
443 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
444
445
446 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC);
447 document.setApplicationDocumentStatus(PERM_APP_DOC_STATUS);
448 document.route("");
449
450 assertTrue(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
451 assertTrue(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL));
452 }
453
454 @Test public void testRecallToActionListAsInitiatorAfterApprovals() throws Exception {
455 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_TEST_DOC);
456 }
457
458 @Test public void testRecallToActionListAsInitiatorWithNotificationAfterApprovals() throws Exception {
459 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NOTIFY_TEST_DOC);
460 }
461
462 @Test public void testRecallToActionListAsInitiatorWithoutPendingNotificationAfterApprovals() throws Exception {
463 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NO_PENDING_NOTIFY_TEST_DOC);
464 }
465
466 @Test public void testRecallToActionListAsInitiatorWithThirdPartyNotificationAfterApprovals() throws Exception {
467 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NOTIFY_THIRDPARTY_TEST_DOC);
468 }
469
470
471
472
473
474 @Test public void testRecallToActionListAsThirdParty() throws Exception {
475 Permission perm = createRecallPermission(RECALL_TEST_DOC, null, null, null);
476
477 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator");
478 KimApiServiceLocator.getRoleService().assignPermissionToRole(perm.getId(), techadmin.getId());
479
480 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC);
481 }
482
483
484 protected void assignRoutePermissionToTechAdmin() {
485
486 Permission routePerm = createRouteDocumentPermission(RECALL_TEST_DOC, null, null, null);
487 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator");
488 KimApiServiceLocator.getRoleService().assignPermissionToRole(routePerm.getId(), techadmin.getId());
489 }
490 protected void assignRecallPermissionToDocumentRouters() {
491
492 Permission recallPerm = createRecallPermission(RECALL_TEST_DOC, null, null, null);
493 Role documentRouterDerivedRole = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-WKFLW", "Document Router");
494 KimApiServiceLocator.getRoleService().assignPermissionToRole(recallPerm.getId(), documentRouterDerivedRole.getId());
495 }
496
497
498
499
500 @Test public void testRoutePermissionAssignmentInsufficientForRouterToRecallDoc() throws Exception {
501 assignRoutePermissionToTechAdmin();
502
503 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC, false);
504 }
505
506
507
508
509 @Test public void testRecallPermissionAssignmentInsufficientForRouterToRecallDoc() throws Exception {
510 assignRecallPermissionToDocumentRouters();
511
512 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC, false);
513 }
514
515
516
517 @Test public void testRecallToActionListAsRouterDerivedRole() throws Exception {
518
519 assignRoutePermissionToTechAdmin();
520 assignRecallPermissionToDocumentRouters();
521
522 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC);
523 }
524
525
526
527
528
529
530
531
532 protected Role createRoleWithRecallPermission(String ns, String name, Permission recallPerm, String kimTypeName, String roleQualifierName) {
533
534 KimAttribute.Builder attribute = KimAttribute.Builder.create("org.kuali.rice.kim.bo.impl.KimAttributes", roleQualifierName, "KR-SYS");
535 attribute.setAttributeLabel(roleQualifierName);
536 attribute.setActive(true);
537 KimAttributeBo customAttribute = KRADServiceLocator.getBusinessObjectService().save(KimAttributeBo.from(attribute.build()));
538
539
540 KimType.Builder kimType = KimType.Builder.create();
541 kimType.setNamespaceCode("KR-SYS");
542 kimType.setName(kimTypeName);
543 kimType.setActive(true);
544 KimTypeAttribute.Builder kimTypeAttribute = KimTypeAttribute.Builder.create();
545 kimTypeAttribute.setKimAttribute(KimAttribute.Builder.create(customAttribute));
546 kimTypeAttribute.setActive(true);
547 kimType.setAttributeDefinitions(Collections.singletonList(kimTypeAttribute));
548 KimTypeBo customKimType = KRADServiceLocator.getBusinessObjectService().save(KimTypeBo.from(kimType.build()));
549
550
551 Role.Builder role = Role.Builder.create();
552 role.setActive(true);
553 role.setDescription("RecallTest custom recall role");
554 role.setName(ns);
555 role.setNamespaceCode(name);
556 role.setKimTypeId(customKimType.getId());
557 Role customRole = KimApiServiceLocator.getRoleService().createRole(role.build());
558
559 KimApiServiceLocator.getRoleService().assignPermissionToRole(recallPerm.getId(), customRole.getId());
560
561 List<String> recallCapableRoleIds = KimApiServiceLocator.getPermissionService().getRoleIdsForPermission(recallPerm.getNamespaceCode(), recallPerm.getName());
562 Assert.assertFalse("No recall-capable roles found", recallCapableRoleIds.isEmpty());
563 Assert.assertTrue("New role is not associated with Recall permission", recallCapableRoleIds.contains(customRole.getId()));
564
565 return customRole;
566 }
567
568
569
570
571
572
573
574
575 protected void assignUserQualifiedRole(String principalId, Role role, String roleQualifierName, String roleQualifierValue) {
576
577 Map<String, String> qualifications = new HashMap<String, String>();
578 qualifications.put(roleQualifierName, roleQualifierValue);
579 KimApiServiceLocator.getRoleService().assignPrincipalToRole(getPrincipalIdForName("arh14"), role.getNamespaceCode(), role.getName(), qualifications);
580
581 Collection<String> ids = KimApiServiceLocator.getRoleService().getRoleMemberPrincipalIds(role.getNamespaceCode(), role.getName(), qualifications);
582 Assert.assertTrue("Qualified role assignment failed", ids.contains(principalId));
583 }
584
585
586
587
588 @Test public void testRecallWithCustomDocumentTypeAuthorizer() throws Exception {
589
590 final String ARH14 = getPrincipalIdForName("arh14");
591
592
593 disableInitiatorRecallPermission();
594
595 RecallTestDocumentTypeAuthorizer.buildPermissionDetailsInvoked = false;
596 RecallTestDocumentTypeAuthorizer.buildRoleQualifiersInvoked = false;
597
598
599 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("arh14"), RECALL_TEST_DOC, false);
600
601 final String RECALL_ROLE_NM = "CustomRecall";
602 final String RECALL_ROLE_NS = "KR-SYS";
603
604
605 Permission recallPerm = createRecallPermission(RECALL_TEST_DOC, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_VALUE, null, null);
606 Role recallRole = createRoleWithRecallPermission(RECALL_ROLE_NM, RECALL_ROLE_NS, recallPerm, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_KIM_TYPE_NAME, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_NAME);
607 assignUserQualifiedRole(ARH14, recallRole, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_NAME, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_VALUE);
608
609 Map<String, String> d = new HashMap<String, String>();
610 d.put(RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_NAME, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_VALUE);
611 d.put(KewApiConstants.DOCUMENT_TYPE_NAME_DETAIL, RECALL_TEST_DOC);
612 d.put(KewApiConstants.ROUTE_NODE_NAME_DETAIL, ROUTE_NODE);
613 d.put(KewApiConstants.DOCUMENT_STATUS_DETAIL, ROUTE_STATUS);
614 Map<String, String> q = new HashMap<String, String>();
615 q.put(RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_NAME, RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_VALUE);
616
617 List<Permission> permissions = KimApiServiceLocator.getPermissionService().getAuthorizedPermissionsByTemplate(ARH14, KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, d, q);
618 Assert.assertEquals(1, permissions.size());
619 Assert.assertEquals(recallPerm.getId(), permissions.get(0).getId());
620
621
622 testRecallToActionListAfterApprovals(EWESTFAL, ARH14, RECALL_TEST_DOC, false);
623
624
625 org.kuali.rice.kew.api.doctype.DocumentType dt = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(RECALL_TEST_DOC);
626 org.kuali.rice.kew.api.doctype.DocumentType.Builder b = org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(dt);
627 b.setAuthorizer(RecallTestDocumentTypeAuthorizer.class.getName());
628
629 KEWServiceLocator.getDocumentTypeService().save(DocumentType.from(b));
630
631 Assert.assertEquals(RecallTestDocumentTypeAuthorizer.class.getName(), KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(RECALL_TEST_DOC).getAuthorizer());
632
633
634 Assert.assertFalse(RecallTestDocumentTypeAuthorizer.buildPermissionDetailsInvoked);
635 Assert.assertFalse(RecallTestDocumentTypeAuthorizer.buildRoleQualifiersInvoked);
636
637
638 testRecallToActionListAfterApprovals(EWESTFAL, ARH14, RECALL_TEST_DOC);
639
640 Assert.assertTrue(RecallTestDocumentTypeAuthorizer.buildPermissionDetailsInvoked);
641 Assert.assertTrue(RecallTestDocumentTypeAuthorizer.buildRoleQualifiersInvoked);
642
643
644 String orig = RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_VALUE;
645 try {
646 RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_VALUE = "I will not match";
647 testRecallToActionListAfterApprovals(EWESTFAL, ARH14, RECALL_TEST_DOC, false);
648 } finally {
649 RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_QUALIFIER_VALUE = orig;
650 }
651
652 orig = RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_VALUE;
653 try {
654 RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_VALUE = "I won't match either";
655 testRecallToActionListAfterApprovals(EWESTFAL, ARH14, RECALL_TEST_DOC, false);
656 } finally {
657 RecallTestDocumentTypeAuthorizer.CUSTOM_RECALL_DETAIL_VALUE = orig;
658 }
659 }
660
661 protected void testRecallToActionListAsInitiatorAfterApprovals(String doctype) {
662 testRecallToActionListAfterApprovals(EWESTFAL, EWESTFAL, doctype);
663 }
664
665
666
667 protected void testRecallToActionListAfterApprovals(String initiator, String recaller, String doctype) {
668 testRecallToActionListAfterApprovals(initiator, recaller, doctype, true);
669 }
670 protected void testRecallToActionListAfterApprovals(String initiator, String recaller, String doctype, boolean expect_recall_success) {
671 boolean notifyPreviousRecipients = !RECALL_TEST_DOC.equals(doctype);
672 boolean notifyPendingRecipients = !RECALL_NO_PENDING_NOTIFY_TEST_DOC.equals(doctype);
673 String[] thirdPartiesNotified = RECALL_NOTIFY_THIRDPARTY_TEST_DOC.equals(doctype) ? new String[] { "quickstart", "admin" } : new String[] {};
674
675 WorkflowDocument document = WorkflowDocumentFactory.createDocument(initiator, doctype);
676 document.route("");
677
678 WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).approve("");
679 WorkflowDocumentFactory.loadDocument(initiator, document.getDocumentId()).approve("");
680 WorkflowDocumentFactory.loadDocument(RKIRKEND, document.getDocumentId()).approve("");
681
682 document = WorkflowDocumentFactory.loadDocument(recaller, document.getDocumentId());
683 System.err.println(document.getValidActions().getValidActions());
684 if (expect_recall_success) {
685 assertTrue("recaller '" + recaller + "' should be able to RECALL", document.getValidActions().getValidActions().contains(ActionType.RECALL));
686 } else {
687 assertFalse("recaller '" + recaller + "' should NOT be able to RECALL", document.getValidActions().getValidActions().contains(ActionType.RECALL));
688 return;
689 }
690 document.recall("recalling", false);
691
692 assertTrue("Document should be saved", document.isSaved());
693 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.COMPLETE);
694
695
696 assertTrue(document.isCompletionRequested());
697
698
699 assertEquals(notifyPendingRecipients, WorkflowDocumentFactory.loadDocument(NATJOHNS, document.getDocumentId()).isFYIRequested());
700
701 assertEquals(notifyPreviousRecipients, WorkflowDocumentFactory.loadDocument(RKIRKEND, document.getDocumentId()).isFYIRequested());
702
703 assertFalse(WorkflowDocumentFactory.loadDocument(initiator, document.getDocumentId()).isFYIRequested());
704
705 assertEquals(notifyPreviousRecipients, WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isFYIRequested());
706
707 if (!ArrayUtils.isEmpty(thirdPartiesNotified)) {
708 for (String recipient: thirdPartiesNotified) {
709 assertTrue("Expected FYI to be sent to: " + recipient, WorkflowDocumentFactory.loadDocument(getPrincipalIdForName(recipient), document.getDocumentId()).isFYIRequested());
710 }
711 }
712
713
714 for (String user: new String[] { RKIRKEND, NATJOHNS }) {
715 WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()).fyi();
716 }
717
718 document.complete("completing");
719
720 assertTrue("Document should be enroute", document.isEnroute());
721
722
723
724 for (String user: new String[] { JHOPF, RKIRKEND, NATJOHNS }) {
725 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId());
726 assertFalse(getPrincipalNameForId(user) + " should not have an FYI", document.isFYIRequested());
727 }
728
729
730 for (String user: new String[] { JHOPF, initiator, RKIRKEND, NATJOHNS, BMCGOUGH }) {
731 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId());
732 assertTrue(getPrincipalNameForId(user) + " should have approval request", document.isApprovalRequested());
733 document.approve("approving");
734 }
735
736
737 assertTrue("Document should be processed", document.isProcessed());
738 assertTrue("Document should be approved", document.isApproved());
739
740 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
741 document.acknowledge("");
742
743 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("jthomas"), document.getDocumentId());
744 document.fyi();
745
746 assertTrue("Document should be approved", document.isApproved());
747 assertTrue("Document should be final", document.isFinal());
748 }
749 }