001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.actionrequest; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.log4j.Logger; 021import org.kuali.rice.core.api.delegation.DelegationType; 022import org.kuali.rice.core.api.exception.RiceRuntimeException; 023import org.kuali.rice.core.api.membership.MemberType; 024import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 025import org.kuali.rice.kew.actionrequest.service.ActionRequestService; 026import org.kuali.rice.kew.api.WorkflowRuntimeException; 027import org.kuali.rice.kew.api.action.ActionRequestPolicy; 028import org.kuali.rice.kew.api.action.ActionRequestStatus; 029import org.kuali.rice.kew.api.action.RecipientType; 030import org.kuali.rice.kew.api.identity.Id; 031import org.kuali.rice.kew.api.user.UserId; 032import org.kuali.rice.kew.api.util.CodeTranslator; 033import org.kuali.rice.kew.engine.RouteContext; 034import org.kuali.rice.kew.engine.node.RouteNodeInstance; 035import org.kuali.rice.kew.identity.service.IdentityHelperService; 036import org.kuali.rice.kew.role.KimRoleRecipient; 037import org.kuali.rice.kew.role.KimRoleResponsibilityRecipient; 038import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 039import org.kuali.rice.kew.rule.ResolvedQualifiedRole; 040import org.kuali.rice.kew.service.KEWServiceLocator; 041import org.kuali.rice.kew.user.RoleRecipient; 042import org.kuali.rice.kew.api.KewApiConstants; 043import org.kuali.rice.kew.util.Utilities; 044import org.kuali.rice.kew.workgroup.GroupId; 045import org.kuali.rice.kim.api.common.delegate.DelegateMember; 046import org.kuali.rice.kim.api.common.delegate.DelegateType; 047import org.kuali.rice.kim.api.group.Group; 048import org.kuali.rice.kim.api.group.GroupService; 049import org.kuali.rice.kim.api.identity.IdentityService; 050import org.kuali.rice.kim.api.identity.principal.Principal; 051import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 052import org.kuali.rice.kim.api.responsibility.ResponsibilityAction; 053import org.kuali.rice.kim.api.role.Role; 054import org.kuali.rice.kim.api.role.RoleMembership; 055import org.kuali.rice.kim.api.role.RoleService; 056import org.kuali.rice.kim.api.services.KimApiServiceLocator; 057import org.kuali.rice.krad.util.KRADConstants; 058import org.kuali.rice.krad.util.ObjectUtils; 059 060import java.sql.Timestamp; 061import java.util.ArrayList; 062import java.util.Collection; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.List; 066import java.util.Map; 067import java.util.Set; 068 069 070/** 071 * A factory to aid in creating the ever-so-gnarly ActionRequestValue object. 072 * 073 * @author Kuali Rice Team (rice.collab@kuali.org) 074 */ 075public class ActionRequestFactory { 076 077 private static final Logger LOG = Logger.getLogger(ActionRequestFactory.class); 078 079 private static RoleService roleService; 080 private static IdentityHelperService identityHelperService; 081 private static IdentityService identityService; 082 private static GroupService groupService; 083 private static ActionRequestService actionRequestService; 084 085 private DocumentRouteHeaderValue document; 086 private RouteNodeInstance routeNode; 087 private List<ActionRequestValue> requestGraphs = new ArrayList<ActionRequestValue>(); 088 089 public ActionRequestFactory() { 090 } 091 092 public ActionRequestFactory(DocumentRouteHeaderValue document) { 093 this.document = document; 094 } 095 096 public ActionRequestFactory(DocumentRouteHeaderValue document, RouteNodeInstance routeNode) { 097 this.document = document; 098 this.routeNode = routeNode; 099 } 100 101 public ActionRequestFactory(RouteContext routeContext) { 102 this(routeContext.getDocument(), routeContext.getNodeInstance()); 103 } 104 105 /** 106 * Constructs ActionRequestValue using default priority and 0 as responsibility 107 * 108 * @param actionRequested 109 * @param recipient 110 * @param description 111 * @param forceAction 112 * @param annotation 113 * @return ActionRequestValue 114 */ 115 public ActionRequestValue createActionRequest(String actionRequested, Recipient recipient, String description, Boolean forceAction, String annotation) { 116 return createActionRequest(actionRequested, new Integer(0), recipient, description, KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID, forceAction, annotation); 117 } 118 119 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String annotation) { 120 return createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, null, null, annotation); 121 } 122 123 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId, String annotation) { 124 return createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, approvePolicy, ruleId, annotation, null); 125 } 126 127 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId, String annotation, String requestLabel) { 128 ActionRequestValue actionRequest = new ActionRequestValue(); 129 actionRequest.setActionRequested(actionRequested); 130 actionRequest.setDocVersion(document.getDocVersion()); 131 actionRequest.setPriority(priority); 132 actionRequest.setRouteHeader(document); 133 actionRequest.setDocumentId(document.getDocumentId()); 134 actionRequest.setRouteLevel(document.getDocRouteLevel()); 135 actionRequest.setNodeInstance(routeNode); 136 actionRequest.setResponsibilityId(responsibilityId); 137 actionRequest.setResponsibilityDesc(description); 138 actionRequest.setApprovePolicy(approvePolicy); 139 actionRequest.setForceAction(forceAction); 140 actionRequest.setRuleBaseValuesId(ruleId); 141 actionRequest.setAnnotation(annotation); 142 actionRequest.setRequestLabel(requestLabel); 143 setDefaultProperties(actionRequest); 144 resolveRecipient(actionRequest, recipient); 145 146 return actionRequest; 147 } 148 149 public ActionRequestValue createBlankActionRequest() { 150 ActionRequestValue request = new ActionRequestValue(); 151 request.setRouteHeader(document); 152 if (document != null) { 153 request.setDocumentId(document.getDocumentId()); 154 } 155 request.setNodeInstance(routeNode); 156 return request; 157 } 158 159 160 public ActionRequestValue createNotificationRequest(String actionRequestCode, PrincipalContract principal, String reasonActionCode, PrincipalContract reasonActionUser, String responsibilityDesc) { 161 ActionRequestValue request = createActionRequest(actionRequestCode, new KimPrincipalRecipient(principal), responsibilityDesc, Boolean.TRUE, null); 162 String annotation = generateNotificationAnnotation(reasonActionUser, actionRequestCode, reasonActionCode, request); 163 request.setAnnotation(annotation); 164 return request; 165 } 166 167 //unify these 2 methods if possible 168 public List<ActionRequestValue> generateNotifications(List requests, PrincipalContract principal, Recipient delegator, 169 String notificationRequestCode, String actionTakenCode) 170 { 171 String groupName = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, 172 KRADConstants.DetailTypes.WORKGROUP_DETAIL_TYPE, 173 KewApiConstants.NOTIFICATION_EXCLUDED_USERS_WORKGROUP_NAME_IND); 174 175 176 Group notifyExclusionWorkgroup = null; 177 if(!StringUtils.isBlank(groupName)){ 178 notifyExclusionWorkgroup = getGroupService().getGroupByNamespaceCodeAndName( 179 Utilities.parseGroupNamespaceCode(groupName), Utilities.parseGroupName(groupName)); 180 } 181 182 183 184 return generateNotifications(null, getActionRequestService().getRootRequests(requests), principal, delegator, notificationRequestCode, actionTakenCode, notifyExclusionWorkgroup); 185 } 186 187 /** 188 * Generates a notification request for each action request specified, filtering out the specified principal 189 * and delegator, and exclusion workgroup members from notification list 190 * @param parentRequest if non-null, attaches generated notification requests to this parent action request 191 * @param requests list of ActionRequestValues for which to generate corresponding notification requests 192 * @param principal principal to exclude from notifications 193 * @param delegator delegator to exclude from notifications 194 * @param notificationRequestCode the actionrequest code of generated notifications 195 * @param actionTakenCode the actiontaken code to display as the cause of the notification generation 196 * @param notifyExclusionWorkgroup workgroup whose members should not be sent notifications 197 * @return a list of generated notification requests 198 */ 199 private List<ActionRequestValue> generateNotifications(ActionRequestValue parentRequest, 200 List requests, PrincipalContract principal, Recipient delegator, String notificationRequestCode, 201 String actionTakenCode, Group notifyExclusionWorkgroup) 202 { 203 List<ActionRequestValue> notificationRequests = new ArrayList<ActionRequestValue>(); 204 for (Iterator iter = requests.iterator(); iter.hasNext();) 205 { 206 ActionRequestValue actionRequest = (ActionRequestValue) iter.next(); 207 if (!(actionRequest.isRecipientRoutedRequest(principal.getPrincipalId()) || actionRequest.isRecipientRoutedRequest(delegator))) 208 { 209 // skip user requests to system users 210 if ((notifyExclusionWorkgroup != null) && 211 (isRecipientInGroup(notifyExclusionWorkgroup, actionRequest.getRecipient()))) 212 { 213 continue; 214 } 215 ActionRequestValue notificationRequest = createNotificationRequest(actionRequest, principal, notificationRequestCode, actionTakenCode); 216 notificationRequests.add(notificationRequest); 217 if (parentRequest != null) 218 { 219 notificationRequest.setParentActionRequest(parentRequest); 220 parentRequest.getChildrenRequests().add(notificationRequest); 221 } 222 notificationRequests.addAll(generateNotifications(notificationRequest, actionRequest.getChildrenRequests(), principal, delegator, notificationRequestCode, actionTakenCode, notifyExclusionWorkgroup)); 223 } 224 } 225 return notificationRequests; 226 } 227 228 private boolean isRecipientInGroup(Group group, Recipient recipient) 229 { 230 boolean isMember = false; 231 232 if(recipient instanceof KimPrincipalRecipient) 233 { 234 String principalId = ((KimPrincipalRecipient) recipient).getPrincipalId(); 235 String groupId = group.getId(); 236 isMember = getGroupService().isMemberOfGroup(principalId, groupId); 237 } 238 else if (recipient instanceof KimGroupRecipient) 239 { 240 String kimRecipientId = ((KimGroupRecipient) recipient).getGroup().getId(); 241 isMember = getGroupService().isGroupMemberOfGroup(kimRecipientId, group.getId() ); 242 } 243 return isMember; 244 } 245 246 private ActionRequestValue createNotificationRequest(ActionRequestValue actionRequest, PrincipalContract reasonPrincipal, String notificationRequestCode, String actionTakenCode) { 247 248 String annotation = generateNotificationAnnotation(reasonPrincipal, notificationRequestCode, actionTakenCode, actionRequest); 249 ActionRequestValue request = createActionRequest(notificationRequestCode, actionRequest.getPriority(), actionRequest.getRecipient(), actionRequest.getResponsibilityDesc(), KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID, Boolean.TRUE, annotation); 250 251 request.setDocVersion(actionRequest.getDocVersion()); 252 request.setApprovePolicy(actionRequest.getApprovePolicy()); 253 request.setRoleName(actionRequest.getRoleName()); 254 request.setQualifiedRoleName(actionRequest.getQualifiedRoleName()); 255 request.setQualifiedRoleNameLabel(actionRequest.getQualifiedRoleNameLabel()); 256 request.setDelegationType(actionRequest.getDelegationType()); 257 return request; 258 } 259 260 private void setDefaultProperties(ActionRequestValue actionRequest) { 261 if (actionRequest.getApprovePolicy() == null) { 262 actionRequest.setApprovePolicy(ActionRequestPolicy.FIRST.getCode()); 263 } 264 actionRequest.setCreateDate(new Timestamp(System.currentTimeMillis())); 265 actionRequest.setCurrentIndicator(Boolean.TRUE); 266 if (actionRequest.getForceAction() == null) { 267 actionRequest.setForceAction(Boolean.FALSE); 268 } 269 if (routeNode != null) { 270 actionRequest.setNodeInstance(routeNode); 271 } 272 actionRequest.setJrfVerNbr(new Integer(0)); 273 actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode()); 274 actionRequest.setRouteHeader(document); 275 actionRequest.setDocumentId(document.getDocumentId()); 276 } 277 278 private static void resolveRecipient(ActionRequestValue actionRequest, Recipient recipient) { 279 if (recipient instanceof KimPrincipalRecipient) { 280 actionRequest.setRecipientTypeCd(RecipientType.PRINCIPAL.getCode()); 281 actionRequest.setPrincipalId(((KimPrincipalRecipient)recipient).getPrincipal().getPrincipalId()); 282 } else if (recipient instanceof KimGroupRecipient) { 283 KimGroupRecipient kimGroupRecipient = (KimGroupRecipient)recipient; 284 actionRequest.setRecipientTypeCd(RecipientType.GROUP.getCode()); 285 actionRequest.setGroupId(kimGroupRecipient.getGroup().getId()); 286 } else if (recipient instanceof RoleRecipient){ 287 RoleRecipient role = (RoleRecipient)recipient; 288 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode()); 289 actionRequest.setRoleName(role.getRoleName()); 290 actionRequest.setQualifiedRoleName(role.getQualifiedRoleName()); 291 ResolvedQualifiedRole qualifiedRole = role.getResolvedQualifiedRole(); 292 if (qualifiedRole != null) { 293 actionRequest.setAnnotation(qualifiedRole.getAnnotation() == null ? "" : qualifiedRole.getAnnotation()); 294 actionRequest.setQualifiedRoleNameLabel(qualifiedRole.getQualifiedRoleLabel()); 295 } 296 Recipient targetRecipient = role.getTarget(); 297 if (role.getTarget() != null) { 298 if (targetRecipient instanceof RoleRecipient) { 299 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId()); 300 } 301 resolveRecipient(actionRequest, role.getTarget()); 302 } 303 } else if (recipient instanceof KimRoleResponsibilityRecipient) { 304 KimRoleResponsibilityRecipient roleResponsibilityRecipient = (KimRoleResponsibilityRecipient)recipient; 305 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode()); 306 actionRequest.setRoleName(roleResponsibilityRecipient.getResponsibilities().get(0).getRoleId()); 307 actionRequest.setQualifiedRoleName( 308 roleResponsibilityRecipient.getResponsibilities().get(0).getResponsibilityName()); 309 // what about qualified role name label? 310// actionRequest.setAnnotation(roleRecipient.getResponsibilities().get(0).getResponsibilityName()); 311 Recipient targetRecipient = roleResponsibilityRecipient.getTarget(); 312 if (targetRecipient != null) { 313 if (targetRecipient instanceof RoleRecipient) { 314 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId()); 315 } 316 resolveRecipient(actionRequest, roleResponsibilityRecipient.getTarget()); 317 } 318 } else if (recipient instanceof KimRoleRecipient) { 319 KimRoleRecipient roleRecipient = (KimRoleRecipient)recipient; 320 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode()); 321 Role role = roleRecipient.getRole(); 322 actionRequest.setRoleName(role.getId()); 323 actionRequest.setQualifiedRoleNameLabel(role.getName()); 324 Recipient targetRecipient = roleRecipient.getTarget(); 325 if (targetRecipient != null) { 326 if (targetRecipient instanceof RoleRecipient) { 327 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId()); 328 } 329 resolveRecipient(actionRequest, targetRecipient); 330 } 331 } 332 } 333 334 /** 335 * Creates a root Role Request 336 * @param role 337 * @param actionRequested 338 * @param approvePolicy 339 * @param priority 340 * @param responsibilityId 341 * @param forceAction 342 * @param description 343 * @param ruleId 344 * @return the created root role request 345 */ 346 public ActionRequestValue addRoleRequest(RoleRecipient role, String actionRequested, String approvePolicy, Integer priority, String responsibilityId, Boolean forceAction, String description, String ruleId) { 347 348 ActionRequestValue requestGraph = createActionRequest(actionRequested, priority, role, description, responsibilityId, forceAction, approvePolicy, ruleId, null); 349 if (role != null && role.getResolvedQualifiedRole() != null && role.getResolvedQualifiedRole().getRecipients() != null) { 350 int legitimateTargets = 0; 351 for (Iterator<Id> iter = role.getResolvedQualifiedRole().getRecipients().iterator(); iter.hasNext();) { 352 Id recipientId = (Id) iter.next(); 353 if (recipientId.isEmpty()) 354 { 355 throw new WorkflowRuntimeException("Failed to resolve id of type " + recipientId.getClass().getName() + " returned from role '" + role.getRoleName() + "'. Id returned contained a null or empty value."); 356 } 357 if (recipientId instanceof UserId) 358 { 359 Principal principal = getIdentityHelperService().getPrincipal((UserId) recipientId); 360 if(ObjectUtils.isNotNull(principal)) { 361 role.setTarget(new KimPrincipalRecipient(principal)); 362 } 363 } else if (recipientId instanceof GroupId) 364 { 365 role.setTarget(new KimGroupRecipient(getIdentityHelperService().getGroup((GroupId) recipientId))); 366 } else 367 { 368 throw new WorkflowRuntimeException("Could not process the given type of id: " + recipientId.getClass()); 369 } 370 if (role.getTarget() != null) 371 { 372 legitimateTargets++; 373 ActionRequestValue request = createActionRequest(actionRequested, priority, role, description, responsibilityId, forceAction, null, ruleId, null); 374 request.setParentActionRequest(requestGraph); 375 requestGraph.getChildrenRequests().add(request); 376 } 377 } 378 if (legitimateTargets == 0) { 379 LOG.warn("Role did not yield any legitimate recipients"); 380 } 381 } else { 382 LOG.warn("Didn't create action requests for action request description '" + description + "' because of null role or null part of role object graph."); 383 } 384 requestGraphs.add(requestGraph); 385 return requestGraph; 386 } 387 388 /** 389 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations. 390 * @param responsibilities 391 * @param approvePolicy 392 */ 393 public void addRoleResponsibilityRequest(List<ResponsibilityAction> responsibilities, String approvePolicy) { 394 if (responsibilities == null || responsibilities.isEmpty()) { 395 LOG.warn("Didn't create action requests for action request description because no responsibilities were defined."); 396 return; 397 } 398 // it's assumed the that all in the list have the same action type code, priority number, etc. 399 String actionTypeCode = responsibilities.get(0).getActionTypeCode(); 400 Integer priority = responsibilities.get(0).getPriorityNumber(); 401 boolean forceAction = responsibilities.get(0).isForceAction(); 402 KimRoleResponsibilityRecipient roleResponsibilityRecipient = new KimRoleResponsibilityRecipient(responsibilities); 403 404 // Creation of a parent graph entry for ???? 405 ActionRequestValue requestGraph = null; 406 StringBuffer parentAnnotation = null; 407 // set to allow for suppression of duplicate annotations on the parent action request 408 Set<String> uniqueChildAnnotations = null; 409 if ( responsibilities.size() > 1 ) { 410 requestGraph = createActionRequest( 411 actionTypeCode, 412 priority, roleResponsibilityRecipient, 413 "", // description 414 KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID, 415 forceAction, 416 approvePolicy, 417 null, // ruleId 418 null );// annotation 419 requestGraphs.add(requestGraph); 420 parentAnnotation = new StringBuffer(); 421 uniqueChildAnnotations = new HashSet<String>( responsibilities.size() ); 422 } 423 StringBuffer annotation = new StringBuffer(); 424 for (ResponsibilityAction responsibility : responsibilities) { 425 if ( LOG.isDebugEnabled() ) { 426 LOG.debug( "Processing Responsibility for action request: " + responsibility ); 427 } 428 // KFSMI-2381 - pull information from KIM to populate annotation 429 annotation.setLength( 0 ); 430 Role role = getRoleService().getRole(responsibility.getRoleId()); 431 annotation.append( role.getNamespaceCode() ).append( ' ' ).append( role.getName() ).append( ' ' ); 432 Map<String, String> qualifier = responsibility.getQualifier(); 433 if ( qualifier != null ) { 434 for ( String key : qualifier.keySet() ) { 435 annotation.append( qualifier.get( key ) ).append( ' ' ); 436 } 437 } 438 if (responsibility.getPrincipalId() != null) { 439 roleResponsibilityRecipient.setTarget(new KimPrincipalRecipient(responsibility.getPrincipalId())); 440 } else if (responsibility.getGroupId() != null) { 441 roleResponsibilityRecipient.setTarget(new KimGroupRecipient(responsibility.getGroupId())); 442 } else { 443 throw new RiceRuntimeException("Failed to identify a group or principal on the given ResponsibilityResolutionInfo:" + responsibility); 444 } 445 String annotationStr = annotation.toString(); 446 ActionRequestValue request = createActionRequest( 447 responsibility.getActionTypeCode(), 448 responsibility.getPriorityNumber(), roleResponsibilityRecipient, 449 responsibility.getParallelRoutingGroupingCode(), // description 450 responsibility.getResponsibilityId(), 451 responsibility.isForceAction(), 452 // If not nested in a parent action request, ensure that the request 453 // is first approve so delegations of this request do not require 454 // ALL_APPROVE as well 455 (responsibilities.size() == 1)?ActionRequestPolicy.FIRST.getCode():approvePolicy, 456 null, // ruleId 457 annotationStr); 458 // if there is only a single request, don't create the nesting structure 459 if ( responsibilities.size() > 1 ) { 460 request.setParentActionRequest(requestGraph); 461 requestGraph.getChildrenRequests().add(request); 462 if ( !uniqueChildAnnotations.contains(annotationStr) ) { 463 parentAnnotation.append( annotationStr ).append( " -- " ); 464 uniqueChildAnnotations.add(annotationStr); 465 } 466 } else { 467 requestGraphs.add(request); 468 } 469 generateKimRoleDelegationRequests(responsibility.getDelegates(), request); 470 471 } 472 if ( responsibilities.size() > 1 ) { 473 requestGraph.setAnnotation( StringUtils.chomp( parentAnnotation.toString(), " -- " ) ); 474 } 475 } 476 477 private String generateRoleResponsibilityDelegateAnnotation(DelegateMember member, boolean isPrincipal, boolean isGroup, ActionRequestValue parentRequest) { 478 StringBuffer annotation = new StringBuffer( "Delegation of: " ); 479 annotation.append( parentRequest.getAnnotation() ); 480 annotation.append( " to " ); 481 if (isPrincipal) { 482 annotation.append( "principal " ); 483 Principal principal = getIdentityService().getPrincipal(member.getMemberId()); 484 if ( principal != null ) { 485 annotation.append( principal.getPrincipalName() ); 486 } else { 487 annotation.append( member.getMemberId() ); 488 } 489 } else if (isGroup) { 490 annotation.append( "group " ); 491 Group group = getGroupService().getGroup( member.getMemberId() ); 492 if ( group != null ) { 493 annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() ); 494 } else { 495 annotation.append( member.getMemberId() ); 496 } 497 } else { 498 annotation.append( "?????? '" ); 499 annotation.append( member.getMemberId() ); 500 annotation.append( "'" ); 501 } 502 return annotation.toString(); 503 } 504 505 public ActionRequestValue addDelegationRoleRequest(ActionRequestValue parentRequest, String approvePolicy, RoleRecipient role, String responsibilityId, Boolean forceAction, DelegationType delegationType, String description, String ruleId) { 506 Recipient parentRecipient = parentRequest.getRecipient(); 507 if (parentRecipient instanceof RoleRecipient) { 508 throw new WorkflowRuntimeException("Cannot delegate on Role Request. It must be a request to a person or workgroup, although that request may be in a role"); 509 } 510 if (! relatedToRoot(parentRequest)) { 511 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory"); 512 } 513 ActionRequestValue delegationRoleRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, approvePolicy, ruleId, null); 514 delegationRoleRequest.setDelegationType(delegationType); 515 int count = 0; 516 for (Iterator<Id> iter = role.getResolvedQualifiedRole().getRecipients().iterator(); iter.hasNext(); count++) { 517 //repeat of createRoleRequest code 518 Id recipientId = iter.next(); 519 if (recipientId.isEmpty()) { 520 throw new WorkflowRuntimeException("Failed to resolve id of type " + recipientId.getClass().getName() + " returned from role '" + role.getRoleName() + "'. Id returned contained a null or empty value."); 521 } 522 if (recipientId instanceof UserId) { 523 role.setTarget(new KimPrincipalRecipient(getIdentityHelperService().getPrincipal((UserId) recipientId))); 524 } else if (recipientId instanceof GroupId) { 525 role.setTarget(new KimGroupRecipient(getIdentityHelperService().getGroup((GroupId) recipientId))); 526 } else { 527 throw new WorkflowRuntimeException("Could not process the given type of id: " + recipientId.getClass()); 528 } 529 ActionRequestValue request = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, null, ruleId, null); 530 request.setDelegationType(delegationType); 531 //end repeat 532 request.setParentActionRequest(delegationRoleRequest); 533 delegationRoleRequest.getChildrenRequests().add(request); 534 } 535 536 //put this mini graph in the larger graph 537 if (count > 0) { 538 parentRequest.getChildrenRequests().add(delegationRoleRequest); 539 delegationRoleRequest.setParentActionRequest(parentRequest); 540 } 541 542 return delegationRoleRequest; 543 } 544 545 /** 546 * Add a delegation request to the given parent action request. 547 * 548 * @param parentRequest the parent request to add it to 549 * @param recipient the recipient to send the delegation request to 550 * @param responsibilityId 551 * @param forceAction 552 * @param delegationType primary or secondary? 553 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 554 * @param annotation the annotation to put on the delegation request 555 * @param ruleId 556 * @return the delegation request that was added 557 */ 558 public ActionRequestValue addDelegationRequest(ActionRequestValue parentRequest, Recipient recipient, String responsibilityId, Boolean forceAction, DelegationType delegationType, String actionRequestPolicyCode, String annotation, String ruleId) { 559 if (! relatedToRoot(parentRequest)) { 560 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory"); 561 } 562 ActionRequestValue delegationRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), recipient, parentRequest.getResponsibilityDesc(), responsibilityId, forceAction, actionRequestPolicyCode, ruleId, annotation); 563 delegationRequest.setDelegationType(delegationType); 564 565 parentRequest.getChildrenRequests().add(delegationRequest); 566 delegationRequest.setParentActionRequest(parentRequest); 567 568 return delegationRequest; 569 } 570 571 /** 572 * Add a delegation request to the given parent action request. 573 * 574 * <p>no action type policy code will be specified.</p> 575 * 576 * @param parentRequest the parent request to add it to 577 * @param recipient the recipient to send the delegation request to 578 * @param responsibilityId 579 * @param forceAction 580 * @param delegationType primary or secondary? 581 * @param annotation the annotation to put on the delegation request 582 * @param ruleId 583 * @return the delegation request that was added 584 */ 585 public ActionRequestValue addDelegationRequest(ActionRequestValue parentRequest, Recipient recipient, String responsibilityId, Boolean forceAction, DelegationType delegationType, String annotation, String ruleId) { 586 return addDelegationRequest(parentRequest, recipient, responsibilityId, forceAction, delegationType, null, annotation, ruleId); 587 } 588 589 //could probably base behavior off of recipient type 590 public ActionRequestValue addRootActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId) { 591 ActionRequestValue requestGraph = createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, approvePolicy, ruleId, null); 592 requestGraphs.add(requestGraph); 593 return requestGraph; 594 } 595 596 /** 597 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations. 598 * 599 * @param actionRequestedCode the type of action requested 600 * @param priority 601 * @param role the role that the members belong to 602 * @param memberships the role members to generate child requests to 603 * @param description 604 * @param responsibilityId 605 * @param forceAction 606 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 607 * @param requestLabel 608 * @return the request generated for the role members 609 */ 610 public ActionRequestValue addKimRoleRequest(String actionRequestedCode, Integer priority, Role role, 611 List<RoleMembership> memberships, String description, String responsibilityId, boolean forceAction, 612 String actionRequestPolicyCode, String requestLabel) { 613 return addKimRoleRequest(actionRequestedCode, priority, role, memberships, description, responsibilityId, 614 forceAction, actionRequestPolicyCode, requestLabel, /* ignoreKimDelegates = */ false); 615 } 616 617 /** 618 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations. 619 * 620 * @param actionRequestedCode the type of action requested 621 * @param priority 622 * @param role the role that the members belong to 623 * @param memberships the role members to generate child requests to 624 * @param description 625 * @param responsibilityId 626 * @param forceAction 627 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 628 * @param requestLabel 629 * @param ignoreKimDelegates should kim delegates be ignored when generating requests 630 * @return the request generated for the role members */ 631 public ActionRequestValue addKimRoleRequest(String actionRequestedCode, Integer priority, Role role, 632 List<RoleMembership> memberships, String description, String responsibilityId, boolean forceAction, 633 String actionRequestPolicyCode, String requestLabel, boolean ignoreKimDelegates) { 634 635 ActionRequestValue roleMemberRequest = null; 636 637 if (CollectionUtils.isEmpty(memberships)) { 638 LOG.warn("Didn't create action requests for action request description because no role members were defined for role id " + role.getId()); 639 return roleMemberRequest; 640 } 641 642 KimRoleRecipient roleRecipient = new KimRoleRecipient(role); 643 644 // Creation of a parent graph entry for ???? 645 ActionRequestValue requestGraph = null; 646 if ( memberships.size() > 1 ) { 647 requestGraph = createActionRequest( 648 actionRequestedCode, 649 priority, 650 roleRecipient, 651 "", // description 652 responsibilityId, 653 forceAction, 654 actionRequestPolicyCode, 655 null, // ruleId 656 null );// annotation 657 requestGraphs.add(requestGraph); 658 } 659 660 for (RoleMembership membership : memberships) { 661 if ( LOG.isDebugEnabled() ) { 662 LOG.debug( "Processing RoleMembership for action request: " + membership ); 663 } 664 665 if (MemberType.PRINCIPAL.equals(membership.getType())) { 666 roleRecipient.setTarget(new KimPrincipalRecipient(membership.getMemberId())); 667 } else if (MemberType.GROUP.equals(membership.getType())) { 668 roleRecipient.setTarget(new KimGroupRecipient(membership.getMemberId())); 669 } else { 670 throw new RiceRuntimeException("Failed to identify a group or principal on the given RoleMembership:" + membership); 671 } 672 673 ActionRequestValue request = createActionRequest( 674 actionRequestedCode, 675 priority, 676 roleRecipient, 677 "", // description 678 responsibilityId, 679 forceAction, 680 // If not nested in a parent action request, ensure that the request 681 // is first approve so delegations of this request do not require 682 // ALL_APPROVE as well 683 (memberships.size() == 1) ? ActionRequestPolicy.FIRST.getCode() : actionRequestPolicyCode, 684 null, // ruleId 685 null); // annotation 686 687 // if there is only a single request, don't create the nesting structure 688 if ( memberships.size() > 1 ) { 689 request.setParentActionRequest(requestGraph); 690 requestGraph.getChildrenRequests().add(request); 691 692 if (roleMemberRequest == null) { 693 roleMemberRequest = requestGraph; 694 } 695 } else { 696 roleMemberRequest = request; 697 requestGraphs.add(request); 698 } 699 700 if (!ignoreKimDelegates) { 701 generateKimRoleDelegationRequests(membership.getDelegates(), request); 702 } 703 } 704 705 return roleMemberRequest; 706 } 707 708 /** 709 * Generates a delegate request to a KIM role. 710 * 711 * <p>In other words, the Role is the delegate. Since delegates in KEW are limited to 1 level, this will ignore 712 * any KIM delegations on the given role.</p> 713 * 714 * @param parentRequest the parent request that the delegate request will be added to 715 * @param actionRequestedCode the type of action requested 716 * @param priority 717 * @param role the role that is being delegated to 718 * @param memberships the role members to generate child requests to 719 * @param description 720 * @param responsibilityId 721 * @param forceAction 722 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 723 * @param requestLabel 724 * @return the delegate request generated for the role members 725 */ 726 public ActionRequestValue addDelegateKimRoleRequest(ActionRequestValue parentRequest, DelegationType delegationType, 727 String actionRequestedCode, Integer priority, Role role, List<RoleMembership> memberships, 728 String description, String responsibilityId, boolean forceAction, String actionRequestPolicyCode, 729 String requestLabel) { 730 731 // This is a modified version of addKimRoleRequest. The methods could probably be combined, 732 // but the signature would be even more out of hand and the usage even more confusing. 733 734 ActionRequestValue delegateRoleRequest = null; 735 736 if (CollectionUtils.isEmpty(memberships)) { 737 LOG.warn("Didn't create action requests for action request description because no role members were defined for role id " + role.getId()); 738 return delegateRoleRequest; 739 } 740 741 KimRoleRecipient roleRecipient = new KimRoleRecipient(role); 742 743 // Creation of a parent graph entry for ???? 744 ActionRequestValue requestGraph = null; 745 if ( memberships.size() > 1 ) { 746 requestGraph = createActionRequest( 747 actionRequestedCode, 748 priority, 749 roleRecipient, 750 "", // description 751 responsibilityId, 752 forceAction, 753 actionRequestPolicyCode, 754 null, // ruleId 755 null );// annotation 756 requestGraphs.add(requestGraph); 757 } 758 759 for (RoleMembership membership : memberships) { 760 if ( LOG.isDebugEnabled() ) { 761 LOG.debug( "Processing RoleMembership for action request: " + membership ); 762 } 763 764 if (MemberType.PRINCIPAL.equals(membership.getType())) { 765 roleRecipient.setTarget(new KimPrincipalRecipient(membership.getMemberId())); 766 } else if (MemberType.GROUP.equals(membership.getType())) { 767 roleRecipient.setTarget(new KimGroupRecipient(membership.getMemberId())); 768 } else { 769 throw new RiceRuntimeException("Failed to identify a group or principal on the given RoleMembership:" + membership); 770 } 771 772 ActionRequestValue request = createActionRequest( 773 actionRequestedCode, 774 priority, 775 roleRecipient, 776 "", // description 777 responsibilityId, 778 forceAction, 779 // If not nested in a parent action request, ensure that the request 780 // is first approve so delegations of this request do not require 781 // ALL_APPROVE as well 782 (memberships.size() == 1) ? ActionRequestPolicy.FIRST.getCode() : actionRequestPolicyCode, 783 null, // ruleId 784 null); // annotation 785 786 // if there is only a single request, don't create the nesting structure 787 if ( memberships.size() > 1 ) { 788 request.setParentActionRequest(requestGraph); 789 requestGraph.getChildrenRequests().add(request); 790 791 if (delegateRoleRequest == null) { 792 delegateRoleRequest = requestGraph; 793 } 794 } else { 795 delegateRoleRequest = request; 796 } 797 } 798 799 delegateRoleRequest.setDelegationType(delegationType); 800 delegateRoleRequest.setParentActionRequest(parentRequest); 801 parentRequest.getChildrenRequests().add(delegateRoleRequest); 802 803 return delegateRoleRequest; 804 } 805 806 private void generateKimRoleDelegationRequests(List<DelegateType> delegates, ActionRequestValue parentRequest) { 807 for (DelegateType delegate : delegates) { 808 for (DelegateMember member : delegate.getMembers()) { 809 Recipient recipient; 810 boolean isPrincipal = MemberType.PRINCIPAL.equals(member.getType()); 811 boolean isGroup = MemberType.GROUP.equals(member.getType()); 812 if (isPrincipal) { 813 recipient = new KimPrincipalRecipient(member.getMemberId()); 814 } else if (isGroup) { 815 recipient = new KimGroupRecipient(member.getMemberId()); 816 } else { 817 throw new RiceRuntimeException("Invalid DelegateInfo memberTypeCode encountered, was '" + member.getType() + "'"); 818 } 819 String delegationAnnotation = generateRoleResponsibilityDelegateAnnotation(member, isPrincipal, isGroup, parentRequest); 820 addDelegationRequest(parentRequest, recipient, delegate.getDelegationId(), parentRequest.getForceAction(), delegate.getDelegationType(), delegationAnnotation, null); 821 } 822 } 823 } 824 825 //return true if requestGraph (root) is in this requests' parents 826 public boolean relatedToRoot(ActionRequestValue request) { 827 int i = 0; 828 while(i < 3) { 829 if (requestGraphs.contains(request)) { 830 return true; 831 } else if (request == null) { 832 return false; 833 } 834 i++; 835 request = request.getParentActionRequest(); 836 } 837 return false; 838 } 839 840 public List<ActionRequestValue> getRequestGraphs() { 841 //clean up all the trailing role requests with no children - 842 requestGraphs.removeAll(cleanUpChildren(requestGraphs)); 843 return requestGraphs; 844 } 845 846 private Collection<ActionRequestValue> cleanUpChildren(Collection<ActionRequestValue> children) { 847 Collection<ActionRequestValue> requestsToRemove = new ArrayList<ActionRequestValue>(); 848 for (ActionRequestValue aChildren : children) 849 { 850 851 if (aChildren.isRoleRequest()) 852 { 853 if (aChildren.getChildrenRequests().isEmpty()) 854 { 855 requestsToRemove.add(aChildren); 856 } else 857 { 858 Collection<ActionRequestValue> childRequestsToRemove = cleanUpChildren(aChildren.getChildrenRequests()); 859 aChildren.getChildrenRequests().removeAll(childRequestsToRemove); 860 } 861 } 862 } 863 return requestsToRemove; 864 } 865 866 private String generateNotificationAnnotation(PrincipalContract principal, String notificationRequestCode, String actionTakenCode, ActionRequestValue request) { 867 String notification = "Action " + CodeTranslator.getActionRequestLabel(notificationRequestCode) + " generated by Workflow because " + principal.getPrincipalName() + " took action " 868 + CodeTranslator.getActionTakenLabel(actionTakenCode); 869 // FIXME: KULRICE-5201 switched rsp_id to a varchar, so the comparison below is no longer valid 870// if (request.getResponsibilityId() != null && request.getResponsibilityId() != 0) { 871 // TODO: KULRICE-5329 Verify that this code below makes sense and is sufficient 872 if (request.getResponsibilityId() != null && !KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID.equals(request.getResponsibilityId())) { 873 notification += " Responsibility " + request.getResponsibilityId(); 874 } 875 if (request.getRuleBaseValuesId() != null) { 876 notification += " Rule Id " + request.getRuleBaseValuesId(); 877 } 878 if (request.getAnnotation() != null && request.getAnnotation().length()!=0){ 879 notification += " " + request.getAnnotation(); 880 } 881 return notification; 882 } 883 884 protected static ActionRequestService getActionRequestService() { 885 if ( actionRequestService == null ) { 886 actionRequestService = KEWServiceLocator.getActionRequestService(); 887 } 888 return actionRequestService; 889 } 890 891 /** 892 * @return the roleService 893 */ 894 protected static RoleService getRoleService() { 895 if ( roleService == null ) { 896 roleService = KimApiServiceLocator.getRoleService(); 897 } 898 return roleService; 899 } 900 901 /** 902 * @return the identityHelperService 903 */ 904 protected static IdentityHelperService getIdentityHelperService() { 905 if ( identityHelperService == null ) { 906 identityHelperService = KEWServiceLocator.getIdentityHelperService(); 907 } 908 return identityHelperService; 909 } 910 911 /** 912 * @return the identityService 913 */ 914 protected static IdentityService getIdentityService() { 915 if ( identityService == null ) { 916 identityService = KimApiServiceLocator.getIdentityService(); 917 } 918 return identityService; 919 } 920 921 protected static GroupService getGroupService() { 922 if ( groupService == null ) { 923 groupService = KimApiServiceLocator.getGroupService(); 924 } 925 return groupService; 926 } 927}