001/** 002 * Copyright 2005-2015 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.KewApiConstants; 027import org.kuali.rice.kew.api.WorkflowRuntimeException; 028import org.kuali.rice.kew.api.action.ActionRequestPolicy; 029import org.kuali.rice.kew.api.action.ActionRequestStatus; 030import org.kuali.rice.kew.api.action.RecipientType; 031import org.kuali.rice.kew.api.identity.Id; 032import org.kuali.rice.kew.api.user.UserId; 033import org.kuali.rice.kew.api.util.CodeTranslator; 034import org.kuali.rice.kew.engine.RouteContext; 035import org.kuali.rice.kew.engine.node.RouteNodeInstance; 036import org.kuali.rice.kew.identity.service.IdentityHelperService; 037import org.kuali.rice.kew.role.KimRoleRecipient; 038import org.kuali.rice.kew.role.KimRoleResponsibilityRecipient; 039import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 040import org.kuali.rice.kew.rule.ResolvedQualifiedRole; 041import org.kuali.rice.kew.service.KEWServiceLocator; 042import org.kuali.rice.kew.user.RoleRecipient; 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.KRADUtils; 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. This method only returns requests that are 190 * "root" requests. 191 * 192 * @param parentRequest if non-null, attaches generated notification requests to this parent action request 193 * @param requests list of ActionRequestValues for which to generate corresponding notification requests 194 * @param principal principal to exclude from notifications 195 * @param delegator delegator to exclude from notifications 196 * @param notificationRequestCode the actionrequest code of generated notifications 197 * @param actionTakenCode the actiontaken code to display as the cause of the notification generation 198 * @param notifyExclusionWorkgroup workgroup whose members should not be sent notifications 199 * @return a list of generated notification requests 200 */ 201 private List<ActionRequestValue> generateNotifications(ActionRequestValue parentRequest, 202 List<ActionRequestValue> requests, PrincipalContract principal, Recipient delegator, String notificationRequestCode, 203 String actionTakenCode, Group notifyExclusionWorkgroup) 204 { 205 List<ActionRequestValue> notificationRequests = new ArrayList<ActionRequestValue>(); 206 for (ActionRequestValue actionRequest : requests) { 207 if (!(actionRequest.isRecipientRoutedRequest(principal.getPrincipalId()) || actionRequest.isRecipientRoutedRequest(delegator))) { 208 // skip user requests to system users 209 if ((notifyExclusionWorkgroup != null) && 210 (isRecipientInGroup(notifyExclusionWorkgroup, actionRequest.getRecipient()))) { 211 continue; 212 } 213 ActionRequestValue notificationRequest = createNotificationRequest(actionRequest, principal, notificationRequestCode, actionTakenCode); 214 if (parentRequest == null) { 215 // we'll only add the request to the returned list if it's a root request since we always save from 216 // the root request and cascade down 217 notificationRequests.add(notificationRequest); 218 } else { 219 notificationRequest.setParentActionRequest(parentRequest); 220 parentRequest.getChildrenRequests().add(notificationRequest); 221 } 222 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(KRADUtils.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 Group group = getGroupService().getGroup(responsibility.getGroupId()); 442 if(group != null){ 443 if(!group.isActive() && !document.getDocumentType().getFailOnInactiveGroup().getPolicyValue()){ 444 roleResponsibilityRecipient.setTarget(null); 445 }else if(getGroupService().getMemberPrincipalIds(group.getId()).isEmpty()){ 446 roleResponsibilityRecipient.setTarget(null); 447 }else { 448 roleResponsibilityRecipient.setTarget(new KimGroupRecipient(group)); 449 } 450 } else { 451 throw new IllegalArgumentException("Attempted to create a KimGroupRecipient with a null Group!"); 452 } 453 } else { 454 throw new RiceRuntimeException("Failed to identify a group or principal on the given ResponsibilityResolutionInfo:" + responsibility); 455 } 456 if(roleResponsibilityRecipient.getTarget() != null){ 457 String annotationStr = annotation.toString(); 458 ActionRequestValue request = createActionRequest( 459 responsibility.getActionTypeCode(), 460 responsibility.getPriorityNumber(), roleResponsibilityRecipient, 461 responsibility.getParallelRoutingGroupingCode(), // description 462 responsibility.getResponsibilityId(), 463 responsibility.isForceAction(), 464 // If not nested in a parent action request, ensure that the request 465 // is first approve so delegations of this request do not require 466 // ALL_APPROVE as well 467 (responsibilities.size() == 1)?ActionRequestPolicy.FIRST.getCode():approvePolicy, 468 null, // ruleId 469 annotationStr); 470 // if there is only a single request, don't create the nesting structure 471 if ( responsibilities.size() > 1 ) { 472 request.setParentActionRequest(requestGraph); 473 requestGraph.getChildrenRequests().add(request); 474 if ( !uniqueChildAnnotations.contains(annotationStr) ) { 475 parentAnnotation.append( annotationStr ).append( " -- " ); 476 uniqueChildAnnotations.add(annotationStr); 477 } 478 } else { 479 requestGraphs.add(request); 480 } 481 generateKimRoleDelegationRequests(responsibility.getDelegates(), request); 482 } 483 } 484 if ( responsibilities.size() > 1 ) { 485 requestGraph.setAnnotation( StringUtils.chomp( parentAnnotation.toString(), " -- " ) ); 486 } 487 } 488 489 private String generateRoleResponsibilityDelegateAnnotation(DelegateMember member, boolean isPrincipal, boolean isGroup, ActionRequestValue parentRequest) { 490 StringBuffer annotation = new StringBuffer( "Delegation of: " ); 491 annotation.append( parentRequest.getAnnotation() ); 492 annotation.append( " to " ); 493 if (isPrincipal) { 494 annotation.append( "principal " ); 495 Principal principal = getIdentityService().getPrincipal(member.getMemberId()); 496 if ( principal != null ) { 497 annotation.append( principal.getPrincipalName() ); 498 } else { 499 annotation.append( member.getMemberId() ); 500 } 501 } else if (isGroup) { 502 annotation.append( "group " ); 503 Group group = getGroupService().getGroup( member.getMemberId() ); 504 if ( group != null ) { 505 annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() ); 506 } else { 507 annotation.append( member.getMemberId() ); 508 } 509 } else { 510 annotation.append( "?????? '" ); 511 annotation.append( member.getMemberId() ); 512 annotation.append( "'" ); 513 } 514 return annotation.toString(); 515 } 516 517 public ActionRequestValue addDelegationRoleRequest(ActionRequestValue parentRequest, String approvePolicy, RoleRecipient role, String responsibilityId, Boolean forceAction, DelegationType delegationType, String description, String ruleId) { 518 Recipient parentRecipient = parentRequest.getRecipient(); 519 if (parentRecipient instanceof RoleRecipient) { 520 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"); 521 } 522 if (! relatedToRoot(parentRequest)) { 523 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory"); 524 } 525 ActionRequestValue delegationRoleRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, approvePolicy, ruleId, null); 526 delegationRoleRequest.setDelegationType(delegationType); 527 int count = 0; 528 for (Iterator<Id> iter = role.getResolvedQualifiedRole().getRecipients().iterator(); iter.hasNext(); count++) { 529 //repeat of createRoleRequest code 530 Id recipientId = iter.next(); 531 if (recipientId.isEmpty()) { 532 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."); 533 } 534 if (recipientId instanceof UserId) { 535 role.setTarget(new KimPrincipalRecipient(getIdentityHelperService().getPrincipal((UserId) recipientId))); 536 } else if (recipientId instanceof GroupId) { 537 role.setTarget(new KimGroupRecipient(getIdentityHelperService().getGroup((GroupId) recipientId))); 538 } else { 539 throw new WorkflowRuntimeException("Could not process the given type of id: " + recipientId.getClass()); 540 } 541 ActionRequestValue request = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, null, ruleId, null); 542 request.setDelegationType(delegationType); 543 //end repeat 544 request.setParentActionRequest(delegationRoleRequest); 545 delegationRoleRequest.getChildrenRequests().add(request); 546 } 547 548 //put this mini graph in the larger graph 549 if (count > 0) { 550 parentRequest.getChildrenRequests().add(delegationRoleRequest); 551 delegationRoleRequest.setParentActionRequest(parentRequest); 552 } 553 554 return delegationRoleRequest; 555 } 556 557 /** 558 * Add a delegation request to the given parent action request. 559 * 560 * <p>no action type policy code can be specified as it only applies to roles.</p> 561 * 562 * @param parentRequest the parent request to add it to 563 * @param recipient the recipient to send the delegation request to 564 * @param responsibilityId 565 * @param forceAction 566 * @param delegationType primary or secondary? 567 * @param annotation the annotation to put on the delegation request 568 * @param ruleId 569 * @return the delegation request that was added 570 */ 571 public ActionRequestValue addDelegationRequest(ActionRequestValue parentRequest, Recipient recipient, String responsibilityId, Boolean forceAction, DelegationType delegationType, String annotation, String ruleId) { 572 if (! relatedToRoot(parentRequest)) { 573 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory"); 574 } 575 ActionRequestValue delegationRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), recipient, parentRequest.getResponsibilityDesc(), responsibilityId, forceAction, null, ruleId, annotation); 576 delegationRequest.setDelegationType(delegationType); 577 578 parentRequest.getChildrenRequests().add(delegationRequest); 579 delegationRequest.setParentActionRequest(parentRequest); 580 581 return delegationRequest; 582 } 583 584 //could probably base behavior off of recipient type 585 public ActionRequestValue addRootActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId) { 586 ActionRequestValue requestGraph = createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, approvePolicy, ruleId, null); 587 requestGraphs.add(requestGraph); 588 return requestGraph; 589 } 590 591 /** 592 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations. 593 * 594 * @param actionRequestedCode the type of action requested 595 * @param priority 596 * @param role the role that the members belong to 597 * @param memberships the role members to generate child requests to 598 * @param description 599 * @param responsibilityId 600 * @param forceAction 601 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 602 * @param requestLabel 603 * @return the request generated for the role members 604 */ 605 public ActionRequestValue addKimRoleRequest(String actionRequestedCode, Integer priority, Role role, 606 List<RoleMembership> memberships, String description, String responsibilityId, boolean forceAction, 607 String actionRequestPolicyCode, String requestLabel) { 608 return addKimRoleRequest(actionRequestedCode, priority, role, memberships, description, responsibilityId, 609 forceAction, actionRequestPolicyCode, requestLabel, /* ignoreKimDelegates = */ false); 610 } 611 612 /** 613 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations. 614 * 615 * @param actionRequestedCode the type of action requested 616 * @param priority 617 * @param role the role that the members belong to 618 * @param memberships the role members to generate child requests to 619 * @param description 620 * @param responsibilityId 621 * @param forceAction 622 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 623 * @param requestLabel 624 * @param ignoreKimDelegates should kim delegates be ignored when generating requests 625 * @return the request generated for the role members */ 626 public ActionRequestValue addKimRoleRequest(String actionRequestedCode, Integer priority, Role role, 627 List<RoleMembership> memberships, String description, String responsibilityId, boolean forceAction, 628 String actionRequestPolicyCode, String requestLabel, boolean ignoreKimDelegates) { 629 630 ActionRequestValue roleMemberRequest = null; 631 632 if (CollectionUtils.isEmpty(memberships)) { 633 LOG.warn("Didn't create action requests for action request description because no role members were defined for role id " + role.getId()); 634 return roleMemberRequest; 635 } 636 637 KimRoleRecipient roleRecipient = new KimRoleRecipient(role); 638 639 // Creation of a parent graph entry for ???? 640 ActionRequestValue requestGraph = null; 641 if ( memberships.size() > 1 ) { 642 requestGraph = createActionRequest( 643 actionRequestedCode, 644 priority, 645 roleRecipient, 646 "", // description 647 responsibilityId, 648 forceAction, 649 actionRequestPolicyCode, 650 null, // ruleId 651 null );// annotation 652 requestGraphs.add(requestGraph); 653 } 654 655 for (RoleMembership membership : memberships) { 656 if ( LOG.isDebugEnabled() ) { 657 LOG.debug( "Processing RoleMembership for action request: " + membership ); 658 } 659 660 if (MemberType.PRINCIPAL.equals(membership.getType())) { 661 roleRecipient.setTarget(new KimPrincipalRecipient(membership.getMemberId())); 662 } else if (MemberType.GROUP.equals(membership.getType())) { 663 roleRecipient.setTarget(new KimGroupRecipient(membership.getMemberId())); 664 } else { 665 throw new RiceRuntimeException("Failed to identify a group or principal on the given RoleMembership:" + membership); 666 } 667 668 ActionRequestValue request = createActionRequest( 669 actionRequestedCode, 670 priority, 671 roleRecipient, 672 "", // description 673 responsibilityId, 674 forceAction, 675 // If not nested in a parent action request, ensure that the request 676 // is first approve so delegations of this request do not require 677 // ALL_APPROVE as well 678 (memberships.size() == 1) ? ActionRequestPolicy.FIRST.getCode() : actionRequestPolicyCode, 679 null, // ruleId 680 null); // annotation 681 682 // if there is only a single request, don't create the nesting structure 683 if ( memberships.size() > 1 ) { 684 request.setParentActionRequest(requestGraph); 685 requestGraph.getChildrenRequests().add(request); 686 687 if (roleMemberRequest == null) { 688 roleMemberRequest = requestGraph; 689 } 690 } else { 691 roleMemberRequest = request; 692 requestGraphs.add(request); 693 } 694 695 if (!ignoreKimDelegates) { 696 generateKimRoleDelegationRequests(membership.getDelegates(), request); 697 } 698 } 699 700 return roleMemberRequest; 701 } 702 703 /** 704 * Generates a delegate request to a KIM role. 705 * 706 * <p>In other words, the Role is the delegate. Since delegates in KEW are limited to 1 level, this will ignore 707 * any KIM delegations on the given role.</p> 708 * 709 * @param parentRequest the parent request that the delegate request will be added to 710 * @param actionRequestedCode the type of action requested 711 * @param priority 712 * @param role the role that is being delegated to 713 * @param memberships the role members to generate child requests to 714 * @param description 715 * @param responsibilityId 716 * @param forceAction 717 * @param actionRequestPolicyCode the action request policy code specifying when the action requests are considered to be completed 718 * @param requestLabel 719 * @return the delegate request generated for the role members 720 */ 721 public ActionRequestValue addDelegateKimRoleRequest(ActionRequestValue parentRequest, DelegationType delegationType, 722 String actionRequestedCode, Integer priority, Role role, List<RoleMembership> memberships, 723 String description, String responsibilityId, boolean forceAction, String actionRequestPolicyCode, 724 String requestLabel) { 725 726 // This is a modified version of addKimRoleRequest. The methods could probably be combined, 727 // but the signature would be even more out of hand and the usage even more confusing. 728 729 ActionRequestValue delegateRoleRequest = null; 730 731 if (CollectionUtils.isEmpty(memberships)) { 732 LOG.warn("Didn't create action requests for action request description because no role members were defined for role id " + role.getId()); 733 return delegateRoleRequest; 734 } 735 736 KimRoleRecipient roleRecipient = new KimRoleRecipient(role); 737 738 // Creation of a parent graph entry for ???? 739 ActionRequestValue requestGraph = null; 740 if ( memberships.size() > 1 ) { 741 requestGraph = createActionRequest( 742 actionRequestedCode, 743 priority, 744 roleRecipient, 745 "", // description 746 responsibilityId, 747 forceAction, 748 actionRequestPolicyCode, 749 null, // ruleId 750 null );// annotation 751 requestGraphs.add(requestGraph); 752 } 753 754 for (RoleMembership membership : memberships) { 755 if ( LOG.isDebugEnabled() ) { 756 LOG.debug( "Processing RoleMembership for action request: " + membership ); 757 } 758 759 if (MemberType.PRINCIPAL.equals(membership.getType())) { 760 roleRecipient.setTarget(new KimPrincipalRecipient(membership.getMemberId())); 761 } else if (MemberType.GROUP.equals(membership.getType())) { 762 roleRecipient.setTarget(new KimGroupRecipient(membership.getMemberId())); 763 } else { 764 throw new RiceRuntimeException("Failed to identify a group or principal on the given RoleMembership:" + membership); 765 } 766 767 ActionRequestValue request = createActionRequest( 768 actionRequestedCode, 769 priority, 770 roleRecipient, 771 "", // description 772 responsibilityId, 773 forceAction, 774 // If not nested in a parent action request, ensure that the request 775 // is first approve so delegations of this request do not require 776 // ALL_APPROVE as well 777 (memberships.size() == 1) ? ActionRequestPolicy.FIRST.getCode() : actionRequestPolicyCode, 778 null, // ruleId 779 null); // annotation 780 781 // if there is only a single request, don't create the nesting structure 782 if ( memberships.size() > 1 ) { 783 request.setParentActionRequest(requestGraph); 784 requestGraph.getChildrenRequests().add(request); 785 786 if (delegateRoleRequest == null) { 787 delegateRoleRequest = requestGraph; 788 } 789 } else { 790 delegateRoleRequest = request; 791 } 792 } 793 794 delegateRoleRequest.setDelegationType(delegationType); 795 delegateRoleRequest.setParentActionRequest(parentRequest); 796 parentRequest.getChildrenRequests().add(delegateRoleRequest); 797 798 return delegateRoleRequest; 799 } 800 801 private void generateKimRoleDelegationRequests(List<DelegateType> delegates, ActionRequestValue parentRequest) { 802 for (DelegateType delegate : delegates) { 803 for (DelegateMember member : delegate.getMembers()) { 804 Recipient recipient; 805 boolean isPrincipal = MemberType.PRINCIPAL.equals(member.getType()); 806 boolean isGroup = MemberType.GROUP.equals(member.getType()); 807 if (isPrincipal) { 808 recipient = new KimPrincipalRecipient(member.getMemberId()); 809 } else if (isGroup) { 810 recipient = new KimGroupRecipient(member.getMemberId()); 811 } else { 812 throw new RiceRuntimeException("Invalid DelegateInfo memberTypeCode encountered, was '" + member.getType() + "'"); 813 } 814 String delegationAnnotation = generateRoleResponsibilityDelegateAnnotation(member, isPrincipal, isGroup, parentRequest); 815 addDelegationRequest(parentRequest, recipient, delegate.getDelegationId(), parentRequest.getForceAction(), delegate.getDelegationType(), delegationAnnotation, null); 816 } 817 } 818 } 819 820 //return true if requestGraph (root) is in this requests' parents 821 public boolean relatedToRoot(ActionRequestValue request) { 822 int i = 0; 823 while(i < 3) { 824 if (requestGraphs.contains(request)) { 825 return true; 826 } else if (request == null) { 827 return false; 828 } 829 i++; 830 request = request.getParentActionRequest(); 831 } 832 return false; 833 } 834 835 public List<ActionRequestValue> getRequestGraphs() { 836 //clean up all the trailing role requests with no children - 837 requestGraphs.removeAll(cleanUpChildren(requestGraphs)); 838 return requestGraphs; 839 } 840 841 private Collection<ActionRequestValue> cleanUpChildren(Collection<ActionRequestValue> children) { 842 Collection<ActionRequestValue> requestsToRemove = new ArrayList<ActionRequestValue>(); 843 for (ActionRequestValue aChildren : children) 844 { 845 846 if (aChildren.isRoleRequest()) 847 { 848 if (aChildren.getChildrenRequests().isEmpty()) 849 { 850 requestsToRemove.add(aChildren); 851 } else 852 { 853 Collection<ActionRequestValue> childRequestsToRemove = cleanUpChildren(aChildren.getChildrenRequests()); 854 aChildren.getChildrenRequests().removeAll(childRequestsToRemove); 855 } 856 } 857 } 858 return requestsToRemove; 859 } 860 861 private String generateNotificationAnnotation(PrincipalContract principal, String notificationRequestCode, String actionTakenCode, ActionRequestValue request) { 862 String notification = "Action " + CodeTranslator.getActionRequestLabel(notificationRequestCode) + " generated by Workflow because " + principal.getPrincipalName() + " took action " 863 + CodeTranslator.getActionTakenLabel(actionTakenCode); 864 // FIXME: KULRICE-5201 switched rsp_id to a varchar, so the comparison below is no longer valid 865// if (request.getResponsibilityId() != null && request.getResponsibilityId() != 0) { 866 // TODO: KULRICE-5329 Verify that this code below makes sense and is sufficient 867 if (request.getResponsibilityId() != null && !KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID.equals(request.getResponsibilityId())) { 868 notification += " Responsibility " + request.getResponsibilityId(); 869 } 870 if (request.getRuleBaseValuesId() != null) { 871 notification += " Rule Id " + request.getRuleBaseValuesId(); 872 } 873 if (request.getAnnotation() != null && request.getAnnotation().length()!=0){ 874 notification += " " + request.getAnnotation(); 875 } 876 return notification; 877 } 878 879 protected static ActionRequestService getActionRequestService() { 880 if ( actionRequestService == null ) { 881 actionRequestService = KEWServiceLocator.getActionRequestService(); 882 } 883 return actionRequestService; 884 } 885 886 /** 887 * @return the roleService 888 */ 889 protected static RoleService getRoleService() { 890 if ( roleService == null ) { 891 roleService = KimApiServiceLocator.getRoleService(); 892 } 893 return roleService; 894 } 895 896 /** 897 * @return the identityHelperService 898 */ 899 protected static IdentityHelperService getIdentityHelperService() { 900 if ( identityHelperService == null ) { 901 identityHelperService = KEWServiceLocator.getIdentityHelperService(); 902 } 903 return identityHelperService; 904 } 905 906 /** 907 * @return the identityService 908 */ 909 protected static IdentityService getIdentityService() { 910 if ( identityService == null ) { 911 identityService = KimApiServiceLocator.getIdentityService(); 912 } 913 return identityService; 914 } 915 916 protected static GroupService getGroupService() { 917 if ( groupService == null ) { 918 groupService = KimApiServiceLocator.getGroupService(); 919 } 920 return groupService; 921 } 922}