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.krad.rules; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.config.property.ConfigurationService; 020import org.kuali.rice.core.api.util.RiceKeyConstants; 021import org.kuali.rice.coreservice.framework.parameter.ParameterConstants; 022import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 023import org.kuali.rice.kew.api.KewApiConstants; 024import org.kuali.rice.kew.api.KewApiServiceLocator; 025import org.kuali.rice.kew.api.doctype.DocumentType; 026import org.kuali.rice.kew.api.doctype.DocumentTypeService; 027import org.kuali.rice.kim.api.KimConstants; 028import org.kuali.rice.kim.api.group.Group; 029import org.kuali.rice.kim.api.group.GroupService; 030import org.kuali.rice.kim.api.identity.Person; 031import org.kuali.rice.kim.api.identity.PersonService; 032import org.kuali.rice.kim.api.permission.PermissionService; 033import org.kuali.rice.kim.api.services.KimApiServiceLocator; 034import org.kuali.rice.krad.bo.AdHocRoutePerson; 035import org.kuali.rice.krad.bo.AdHocRouteRecipient; 036import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 037import org.kuali.rice.krad.bo.DocumentHeader; 038import org.kuali.rice.krad.bo.Note; 039import org.kuali.rice.krad.document.Document; 040import org.kuali.rice.krad.maintenance.MaintenanceDocument; 041import org.kuali.rice.krad.document.TransactionalDocument; 042import org.kuali.rice.krad.rules.rule.AddAdHocRoutePersonRule; 043import org.kuali.rice.krad.rules.rule.AddAdHocRouteWorkgroupRule; 044import org.kuali.rice.krad.rules.rule.AddNoteRule; 045import org.kuali.rice.krad.rules.rule.ApproveDocumentRule; 046import org.kuali.rice.krad.rules.rule.CompleteDocumentRule; 047import org.kuali.rice.krad.rules.rule.RouteDocumentRule; 048import org.kuali.rice.krad.rules.rule.SaveDocumentRule; 049import org.kuali.rice.krad.rules.rule.SendAdHocRequestsRule; 050import org.kuali.rice.krad.rules.rule.event.ApproveDocumentEvent; 051import org.kuali.rice.krad.service.DataDictionaryService; 052import org.kuali.rice.krad.service.DictionaryValidationService; 053import org.kuali.rice.krad.service.DocumentDictionaryService; 054import org.kuali.rice.krad.service.KRADServiceLocator; 055import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 056import org.kuali.rice.krad.util.GlobalVariables; 057import org.kuali.rice.krad.util.KRADConstants; 058import org.kuali.rice.krad.util.KRADPropertyConstants; 059import org.kuali.rice.krad.util.KRADUtils; 060import org.kuali.rice.krad.util.MessageMap; 061import org.kuali.rice.krad.util.ObjectUtils; 062import org.kuali.rice.krad.util.RouteToCompletionUtil; 063import org.kuali.rice.krad.workflow.service.WorkflowDocumentService; 064 065import java.util.HashMap; 066import java.util.List; 067import java.util.Map; 068 069/** 070 * Contains all of the business rules that are common to all documents 071 * 072 * @author Kuali Rice Team (rice.collab@kuali.org) 073 */ 074public abstract class DocumentRuleBase implements SaveDocumentRule, RouteDocumentRule, ApproveDocumentRule, AddNoteRule, 075 AddAdHocRoutePersonRule, AddAdHocRouteWorkgroupRule, SendAdHocRequestsRule, CompleteDocumentRule { 076 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentRuleBase.class); 077 078 private static PersonService personService; 079 private static DictionaryValidationService dictionaryValidationService; 080 private static DocumentDictionaryService documentDictionaryService; 081 private static ConfigurationService kualiConfigurationService; 082 private static GroupService groupService; 083 private static PermissionService permissionService; 084 private static DocumentTypeService documentTypeService; 085 private static DataDictionaryService dataDictionaryService; 086 087 // just some arbitrarily high max depth that's unlikely to occur in real life to prevent recursion problems 088 private int maxDictionaryValidationDepth = 100; 089 090 /** 091 * Verifies that the document's overview fields are valid - it does required and format checks. 092 * 093 * @param document 094 * @return boolean True if the document description is valid, false otherwise. 095 */ 096 public boolean isDocumentOverviewValid(Document document) { 097 // add in the documentHeader path 098 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 099 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_HEADER_PROPERTY_NAME); 100 101 // check the document header for fields like the description 102 getDictionaryValidationService().validateBusinessObject(document.getDocumentHeader()); 103 validateSensitiveDataValue(KRADPropertyConstants.EXPLANATION, document.getDocumentHeader().getExplanation(), 104 getDataDictionaryService().getAttributeLabel(DocumentHeader.class, KRADPropertyConstants.EXPLANATION)); 105 106 // drop the error path keys off now 107 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_HEADER_PROPERTY_NAME); 108 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 109 110 return GlobalVariables.getMessageMap().hasNoErrors(); 111 } 112 113 /** 114 * Validates the document attributes against the data dictionary. 115 * 116 * @param document 117 * @param validateRequired if true, then an error will be retruned if a DD required field is empty. if false, no 118 * required 119 * checking is done 120 * @return True if the document attributes are valid, false otherwise. 121 */ 122 public boolean isDocumentAttributesValid(Document document, boolean validateRequired) { 123 // start updating the error path name 124 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 125 126 // check the document for fields like explanation and org doc # 127 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, 128 getMaxDictionaryValidationDepth(), validateRequired); 129 130 // drop the error path keys off now 131 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 132 133 return GlobalVariables.getMessageMap().hasNoErrors(); 134 } 135 136 /** 137 * Runs all business rules needed prior to saving. This includes both common rules for all documents, plus 138 * class-specific 139 * business rules. This method will only return false if it fails the isValidForSave() test. Otherwise, it will 140 * always return 141 * positive regardless of the outcome of the business rules. However, any error messages resulting from the business 142 * rules will 143 * still be populated, for display to the consumer of this service. 144 * 145 * @see org.kuali.rice.krad.rules.rule.SaveDocumentRule#processSaveDocument(org.kuali.rice.krad.document.Document) 146 */ 147 public boolean processSaveDocument(Document document) { 148 boolean isValid = true; 149 150 isValid = isDocumentOverviewValid(document); 151 152 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 153 154 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, 155 getMaxDictionaryValidationDepth(), false); 156 getDictionaryValidationService().validateDefaultExistenceChecksForTransDoc((TransactionalDocument) document); 157 158 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 159 160 isValid &= GlobalVariables.getMessageMap().hasNoErrors(); 161 isValid &= processCustomSaveDocumentBusinessRules(document); 162 163 return isValid; 164 } 165 166 /** 167 * This method should be overridden by children rule classes as a hook to implement document specific business rule 168 * checks for 169 * the "save document" event. 170 * 171 * @param document 172 * @return boolean True if the rules checks passed, false otherwise. 173 */ 174 protected boolean processCustomSaveDocumentBusinessRules(Document document) { 175 return true; 176 } 177 178 /** 179 * Runs all business rules needed prior to routing. This includes both common rules for all maintenance documents, 180 * plus 181 * class-specific business rules. This method will return false if any business rule fails, or if the document is in 182 * an invalid 183 * state, and not routable (see isDocumentValidForRouting()). 184 * 185 * @see org.kuali.rice.krad.rules.rule.RouteDocumentRule#processRouteDocument(org.kuali.rice.krad.document.Document) 186 */ 187 public boolean processRouteDocument(Document document) { 188 boolean isValid = true; 189 190 isValid = isDocumentOverviewValid(document); 191 192 boolean completeRequestPending = RouteToCompletionUtil.checkIfAtleastOneAdHocCompleteRequestExist(document); 193 194 // Validate the document if the header is valid and no pending completion requests 195 if (isValid && !completeRequestPending) { 196 isValid &= isDocumentAttributesValid(document, true); 197 isValid &= processCustomRouteDocumentBusinessRules(document); 198 } 199 200 return isValid; 201 } 202 203 /** 204 * This method should be overridden by children rule classes as a hook to implement document specific business rule 205 * checks for 206 * the "route document" event. 207 * 208 * @param document 209 * @return boolean True if the rules checks passed, false otherwise. 210 */ 211 protected boolean processCustomRouteDocumentBusinessRules(Document document) { 212 return true; 213 } 214 215 /** 216 * Runs all business rules needed prior to approving. This includes both common rules for all documents, plus 217 * class-specific 218 * business rules. This method will return false if any business rule fails, or if the document is in an invalid 219 * state, and not 220 * approveble. 221 * 222 * @see org.kuali.rice.krad.rules.rule.ApproveDocumentRule#processApproveDocument(org.kuali.rice.krad.rules.rule.event.ApproveDocumentEvent) 223 */ 224 public boolean processApproveDocument(ApproveDocumentEvent approveEvent) { 225 boolean isValid = true; 226 227 isValid = processCustomApproveDocumentBusinessRules(approveEvent); 228 229 return isValid; 230 } 231 232 /** 233 * This method should be overridden by children rule classes as a hook to implement document specific business rule 234 * checks for 235 * the "approve document" event. 236 * 237 * @param approveEvent 238 * @return boolean True if the rules checks passed, false otherwise. 239 */ 240 protected boolean processCustomApproveDocumentBusinessRules(ApproveDocumentEvent approveEvent) { 241 return true; 242 } 243 244 /** 245 * Runs all business rules needed prior to adding a document note. This method will return false if any business 246 * rule fails 247 */ 248 public boolean processAddNote(Document document, Note note) { 249 boolean isValid = true; 250 251 isValid &= isNoteValid(note); 252 isValid &= processCustomAddNoteBusinessRules(document, note); 253 254 return isValid; 255 } 256 257 /** 258 * Verifies that the note's fields are valid - it does required and format checks. 259 * 260 * @param note 261 * @return boolean True if the document description is valid, false otherwise. 262 */ 263 public boolean isNoteValid(Note note) { 264 // add the error path keys on the stack 265 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME); 266 267 // check the document header for fields like the description 268 getDictionaryValidationService().validateBusinessObject(note); 269 270 validateSensitiveDataValue(KRADConstants.NOTE_TEXT_PROPERTY_NAME, note.getNoteText(), 271 getDataDictionaryService().getAttributeLabel(Note.class, KRADConstants.NOTE_TEXT_PROPERTY_NAME)); 272 273 // drop the error path keys off now 274 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME); 275 276 return GlobalVariables.getMessageMap().hasNoErrors(); 277 } 278 279 /** 280 * This method should be overridden by children rule classes as a hook to implement document specific business rule 281 * checks for 282 * the "add document note" event. 283 * 284 * @param document 285 * @param note 286 * @return boolean True if the rules checks passed, false otherwise. 287 */ 288 protected boolean processCustomAddNoteBusinessRules(Document document, Note note) { 289 return true; 290 } 291 292 /** 293 * @see org.kuali.rice.krad.rules.rule.AddAdHocRoutePersonRule#processAddAdHocRoutePerson(org.kuali.rice.krad.document.Document, 294 * org.kuali.rice.krad.bo.AdHocRoutePerson) 295 */ 296 public boolean processAddAdHocRoutePerson(Document document, AdHocRoutePerson adHocRoutePerson) { 297 boolean isValid = true; 298 299 isValid &= isAddHocRoutePersonValid(document, adHocRoutePerson); 300 301 isValid &= processCustomAddAdHocRoutePersonBusinessRules(document, adHocRoutePerson); 302 return isValid; 303 } 304 305 /** 306 * @see org.kuali.rice.krad.rules.rule.SendAdHocRequestsRule#processSendAdHocRequests(org.kuali.rice.krad.document.Document) 307 */ 308 public boolean processSendAdHocRequests(Document document) { 309 boolean isValid = true; 310 311 isValid &= isAdHocRouteRecipientsValid(document); 312 isValid &= processCustomSendAdHocRequests(document); 313 314 return isValid; 315 } 316 317 protected boolean processCustomSendAdHocRequests(Document document) { 318 return true; 319 } 320 321 /** 322 * Checks the adhoc route recipient list to ensure there are recipients or 323 * else throws an error that at least one recipient is required. 324 * 325 * @param document 326 * @return 327 */ 328 protected boolean isAdHocRouteRecipientsValid(Document document) { 329 boolean isValid = true; 330 MessageMap errorMap = GlobalVariables.getMessageMap(); 331 332 if (errorMap.getErrorPath().size() == 0) { 333 // add the error path keys on the stack 334 errorMap.addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 335 } 336 337 if ((document.getAdHocRoutePersons() == null || document.getAdHocRoutePersons().isEmpty()) && (document 338 .getAdHocRouteWorkgroups() == null || document.getAdHocRouteWorkgroups().isEmpty())) { 339 340 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, "error.adhoc.missing.recipients"); 341 isValid = false; 342 } 343 344 // drop the error path keys off now 345 errorMap.removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 346 347 return isValid; 348 } 349 350 /** 351 * Verifies that the adHocRoutePerson's fields are valid - it does required and format checks. 352 * 353 * @param person 354 * @return boolean True if valid, false otherwise. 355 */ 356 public boolean isAddHocRoutePersonValid(Document document, AdHocRoutePerson person) { 357 MessageMap errorMap = GlobalVariables.getMessageMap(); 358 359 // new recipients are not embedded in the error path; existing lines should be 360 if (errorMap.getErrorPath().size() == 0) { 361 // add the error path keys on the stack 362 errorMap.addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 363 } 364 365 String actionRequestedCode = person.getActionRequested(); 366 if (StringUtils.isNotBlank(person.getId())) { 367 Person user = getPersonService().getPersonByPrincipalName(person.getId()); 368 369 if (user == null) { 370 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 371 RiceKeyConstants.ERROR_INVALID_ADHOC_PERSON_ID); 372 } 373 else if (!getPermissionService().hasPermission(user.getPrincipalId(), 374 KimConstants.KIM_TYPE_DEFAULT_NAMESPACE, KimConstants.PermissionNames.LOG_IN)) { 375 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 376 RiceKeyConstants.ERROR_INACTIVE_ADHOC_PERSON_ID); 377 } 378 else if(this.isAdHocRouteCompletionToInitiator(document, user, actionRequestedCode)){ 379 // KULRICE-7419: Adhoc route completion validation rule (should not route to initiator for completion) 380 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 381 RiceKeyConstants.ERROR_ADHOC_COMPLETE_PERSON_IS_INITIATOR); 382 } 383 else if(StringUtils.equals(actionRequestedCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ) && this.hasAdHocRouteCompletion(document, person)){ 384 // KULRICE-8760: Multiple complete adhoc requests should not be allowed on the same document 385 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 386 RiceKeyConstants.ERROR_ADHOC_COMPLETE_MORE_THAN_ONE); 387 } 388 else { 389 Class docOrBoClass = null; 390 if (document instanceof MaintenanceDocument) { 391 docOrBoClass = ((MaintenanceDocument) document).getNewMaintainableObject().getDataObjectClass(); 392 } else { 393 docOrBoClass = document.getClass(); 394 } 395 396 if (!getDocumentDictionaryService().getDocumentAuthorizer(document).canReceiveAdHoc(document, user, actionRequestedCode)) { 397 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 398 RiceKeyConstants.ERROR_UNAUTHORIZED_ADHOC_PERSON_ID); 399 } 400 } 401 } else { 402 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 403 RiceKeyConstants.ERROR_MISSING_ADHOC_PERSON_ID); 404 } 405 406 // drop the error path keys off now 407 errorMap.removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 408 409 return GlobalVariables.getMessageMap().hasNoErrors(); 410 } 411 412 /** 413 * KULRICE-7419: Adhoc route completion validation rule (should not route to initiator for completion) 414 * 415 * determine whether the document initiator is the same as the adhoc recipient for completion 416 */ 417 protected boolean isAdHocRouteCompletionToInitiator(Document document, Person person, String actionRequestCode){ 418 if(!StringUtils.equals(actionRequestCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ)){ 419 return false; 420 } 421 422 String documentInitiator = document.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(); 423 String adhocRecipient = person.getPrincipalId(); 424 425 return StringUtils.equals(documentInitiator, adhocRecipient); 426 } 427 428 /** 429 * KULRICE-8760: check whether there is any other complete adhoc request on the given document 430 */ 431 protected boolean hasAdHocRouteCompletion(Document document, AdHocRouteRecipient adHocRouteRecipient){ 432 List<AdHocRoutePerson> adHocRoutePersons = document.getAdHocRoutePersons(); 433 if(ObjectUtils.isNotNull(adHocRoutePersons)){ 434 for(AdHocRoutePerson adhocRecipient : adHocRoutePersons){ 435 // the given adhoc route recipient doesn't count 436 if(adHocRouteRecipient==adhocRecipient){ 437 continue; 438 } 439 440 String actionRequestCode = adhocRecipient.getActionRequested(); 441 if(StringUtils.equals(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, actionRequestCode)){ 442 return true; 443 } 444 } 445 } 446 447 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = document.getAdHocRouteWorkgroups(); 448 if(ObjectUtils.isNotNull(adHocRouteWorkgroups)){ 449 for(AdHocRouteWorkgroup adhocRecipient : adHocRouteWorkgroups){ 450 // the given adhoc route recipient doesn't count 451 if(adHocRouteRecipient==adhocRecipient){ 452 continue; 453 } 454 455 String actionRequestCode = adhocRecipient.getActionRequested(); 456 if(StringUtils.equals(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, actionRequestCode)){ 457 return true; 458 } 459 } 460 } 461 462 return false; 463 } 464 465 /** 466 * This method should be overridden by children rule classes as a hook to implement document specific business rule 467 * checks for 468 * the "add ad hoc route person" event. 469 * 470 * @param document 471 * @param person 472 * @return boolean True if the rules checks passed, false otherwise. 473 */ 474 protected boolean processCustomAddAdHocRoutePersonBusinessRules(Document document, AdHocRoutePerson person) { 475 return true; 476 } 477 478 /** 479 * @see org.kuali.rice.krad.rules.rule.AddAdHocRouteWorkgroupRule#processAddAdHocRouteWorkgroup(org.kuali.rice.krad.document.Document, 480 * org.kuali.rice.krad.bo.AdHocRouteWorkgroup) 481 */ 482 public boolean processAddAdHocRouteWorkgroup(Document document, AdHocRouteWorkgroup adHocRouteWorkgroup) { 483 boolean isValid = true; 484 485 isValid &= isAddHocRouteWorkgroupValid(document, adHocRouteWorkgroup); 486 487 isValid &= processCustomAddAdHocRouteWorkgroupBusinessRules(document, adHocRouteWorkgroup); 488 return isValid; 489 } 490 491 /** 492 * Verifies that the adHocRouteWorkgroup's fields are valid - it does required and format checks. 493 * 494 * @param workgroup 495 * @return boolean True if valid, false otherwise. 496 */ 497 public boolean isAddHocRouteWorkgroupValid(Document document, AdHocRouteWorkgroup workgroup) { 498 MessageMap errorMap = GlobalVariables.getMessageMap(); 499 500 // new recipients are not embedded in the error path; existing lines should be 501 if (errorMap.getErrorPath().size() == 0) { 502 // add the error path keys on the stack 503 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME); 504 } 505 506 if (workgroup.getRecipientName() != null && workgroup.getRecipientNamespaceCode() != null) { 507 // validate that they are a workgroup from the workgroup service by looking them up 508 try { 509 Group group = getGroupService().getGroupByNamespaceCodeAndName(workgroup.getRecipientNamespaceCode(), 510 workgroup.getRecipientName()); 511 512 String actionRequestedCode = workgroup.getActionRequested(); 513 if (group == null || !group.isActive()) { 514 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 515 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 516 RiceKeyConstants.ERROR_INVALID_ADHOC_WORKGROUP_ID); 517 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 518 } 519 else if(StringUtils.equals(actionRequestedCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ) && this.hasAdHocRouteCompletion(document, workgroup)){ 520 // KULRICE-8760: Multiple complete adhoc requests should not be allowed on the same document 521 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, 522 RiceKeyConstants.ERROR_ADHOC_COMPLETE_MORE_THAN_ONE); 523 } 524 else { 525 org.kuali.rice.kew.api.document.WorkflowDocumentService 526 wds = KewApiServiceLocator.getWorkflowDocumentService(); 527 DocumentType documentType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName( 528 wds.getDocument(document.getDocumentNumber()).getDocumentTypeName()); 529 Map<String, String> permissionDetails = buildDocumentTypeActionRequestPermissionDetails( 530 documentType, workgroup.getActionRequested()); 531 if (useKimPermission(KewApiConstants.KEW_NAMESPACE, KewApiConstants.AD_HOC_REVIEW_PERMISSION, permissionDetails) ){ 532 List<String> principalIds = getGroupService().getMemberPrincipalIds(group.getId()); 533 // if any member of the group is not allowed to receive the request, then the group may not receive it 534 for (String principalId : principalIds) { 535 if (!getPermissionService().isAuthorizedByTemplate(principalId, 536 KewApiConstants.KEW_NAMESPACE, KewApiConstants.AD_HOC_REVIEW_PERMISSION, 537 permissionDetails, new HashMap<String, String>())) { 538 539 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 540 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 541 RiceKeyConstants.ERROR_UNAUTHORIZED_ADHOC_WORKGROUP_ID); 542 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 543 544 break; 545 } 546 } 547 } 548 } 549 } catch (Exception e) { 550 LOG.error("isAddHocRouteWorkgroupValid(AdHocRouteWorkgroup)", e); 551 552 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 553 RiceKeyConstants.ERROR_INVALID_ADHOC_WORKGROUP_ID); 554 555 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 556 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 557 } 558 } else { 559 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 560 if(workgroup.getRecipientNamespaceCode()==null) { 561 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE_MISSING); 562 } 563 564 if(workgroup.getRecipientName()==null) { 565 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 566 RiceKeyConstants.ERROR_MISSING_ADHOC_WORKGROUP_ID); 567 } 568 } 569 570 // drop the error path keys off now 571 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME); 572 573 return GlobalVariables.getMessageMap().hasNoErrors(); 574 } 575 /** 576 * This method should be overridden by children rule classes as a hook to implement document specific business rule 577 * checks for 578 * the "add ad hoc route workgroup" event. 579 * 580 * @param document 581 * @param workgroup 582 * @return boolean True if the rules checks passed, false otherwise. 583 */ 584 protected boolean processCustomAddAdHocRouteWorkgroupBusinessRules(Document document, 585 AdHocRouteWorkgroup workgroup) { 586 return true; 587 } 588 589 /** 590 * Gets the maximum number of levels the data-dictionary based validation will recurse for the document 591 */ 592 public int getMaxDictionaryValidationDepth() { 593 return this.maxDictionaryValidationDepth; 594 } 595 596 /** 597 * Gets the maximum number of levels the data-dictionary based validation will recurse for the document 598 */ 599 public void setMaxDictionaryValidationDepth(int maxDictionaryValidationDepth) { 600 if (maxDictionaryValidationDepth < 0) { 601 LOG.error("Dictionary validation depth should be greater than or equal to 0. Value received was: " 602 + maxDictionaryValidationDepth); 603 throw new RuntimeException( 604 "Dictionary validation depth should be greater than or equal to 0. Value received was: " 605 + maxDictionaryValidationDepth); 606 } 607 this.maxDictionaryValidationDepth = maxDictionaryValidationDepth; 608 } 609 610 protected boolean validateSensitiveDataValue(String fieldName, String fieldValue, String fieldLabel) { 611 boolean dataValid = true; 612 613 if (fieldValue == null) { 614 return dataValid; 615 } 616 617 boolean patternFound = KRADUtils.containsSensitiveDataPatternMatch(fieldValue); 618 boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 619 KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT, 620 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND); 621 if (patternFound && !warnForSensitiveData) { 622 dataValid = false; 623 GlobalVariables.getMessageMap().putError(fieldName, 624 RiceKeyConstants.ERROR_DOCUMENT_FIELD_CONTAINS_POSSIBLE_SENSITIVE_DATA, fieldLabel); 625 } 626 627 return dataValid; 628 } 629 630 /** 631 * Business rules check will include all save action rules and any custom rules required by the document specific rule implementation 632 * 633 * @param document Document 634 * @return true if all validations are passed 635 */ 636 public boolean processCompleteDocument(Document document) { 637 boolean isValid = true; 638 isValid &= processSaveDocument(document); 639 isValid &= processCustomCompleteDocumentBusinessRules(document); 640 return isValid; 641 } 642 643 /** 644 * Hook method for deriving business rule classes to provide custom validations required during completion action 645 * 646 * @param document 647 * @return default is true 648 */ 649 protected boolean processCustomCompleteDocumentBusinessRules(Document document) { 650 return true; 651 } 652 653 protected boolean useKimPermission(String namespace, String permissionTemplateName, Map<String, String> permissionDetails) { 654 Boolean b = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND); 655 if (b == null || b) { 656 return getPermissionService().isPermissionDefinedByTemplate(namespace, permissionTemplateName, 657 permissionDetails); 658 } 659 return false; 660 } 661 protected Map<String, String> buildDocumentTypeActionRequestPermissionDetails(DocumentType documentType, String actionRequestCode) { 662 Map<String, String> details = buildDocumentTypePermissionDetails(documentType); 663 if (!StringUtils.isBlank(actionRequestCode)) { 664 details.put(KewApiConstants.ACTION_REQUEST_CD_DETAIL, actionRequestCode); 665 } 666 return details; 667 } 668 669 protected Map<String, String> buildDocumentTypePermissionDetails(DocumentType documentType) { 670 Map<String, String> details = new HashMap<String, String>(); 671 details.put(KewApiConstants.DOCUMENT_TYPE_NAME_DETAIL, documentType.getName()); 672 return details; 673 } 674 675 protected DataDictionaryService getDataDictionaryService() { 676 if (dataDictionaryService == null) { 677 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 678 } 679 return dataDictionaryService; 680 } 681 682 protected PersonService getPersonService() { 683 if (personService == null) { 684 personService = KimApiServiceLocator.getPersonService(); 685 } 686 return personService; 687 } 688 689 public static GroupService getGroupService() { 690 if (groupService == null) { 691 groupService = KimApiServiceLocator.getGroupService(); 692 } 693 return groupService; 694 } 695 696 public static PermissionService getPermissionService() { 697 if (permissionService == null) { 698 permissionService = KimApiServiceLocator.getPermissionService(); 699 } 700 return permissionService; 701 } 702 703 protected DictionaryValidationService getDictionaryValidationService() { 704 if (dictionaryValidationService == null) { 705 dictionaryValidationService = KRADServiceLocatorWeb.getDictionaryValidationService(); 706 } 707 return dictionaryValidationService; 708 } 709 710 protected ConfigurationService getKualiConfigurationService() { 711 if (kualiConfigurationService == null) { 712 kualiConfigurationService = KRADServiceLocator.getKualiConfigurationService(); 713 } 714 return kualiConfigurationService; 715 } 716 717 protected static DocumentDictionaryService getDocumentDictionaryService() { 718 if (documentDictionaryService == null) { 719 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 720 } 721 return documentDictionaryService; 722 } 723 724 public static void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 725 DocumentRuleBase.documentDictionaryService = documentDictionaryService; 726 } 727}