001 /** 002 * Copyright 2005-2012 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 */ 016 package org.kuali.rice.kim.document.rule; 017 018 import org.apache.commons.collections.CollectionUtils; 019 import org.apache.commons.lang.StringUtils; 020 import org.kuali.rice.core.api.uif.RemotableAttributeError; 021 import org.kuali.rice.core.api.util.RiceKeyConstants; 022 import org.kuali.rice.kim.api.identity.IdentityService; 023 import org.kuali.rice.kim.api.identity.entity.EntityDefault; 024 import org.kuali.rice.kim.api.identity.principal.Principal; 025 import org.kuali.rice.kim.api.role.RoleService; 026 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 027 import org.kuali.rice.kim.api.type.KimAttributeField; 028 import org.kuali.rice.kim.api.type.KimType; 029 import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember; 030 import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier; 031 import org.kuali.rice.kim.bo.ui.PersonDocumentAffiliation; 032 import org.kuali.rice.kim.bo.ui.PersonDocumentBoDefaultBase; 033 import org.kuali.rice.kim.bo.ui.PersonDocumentEmploymentInfo; 034 import org.kuali.rice.kim.bo.ui.PersonDocumentGroup; 035 import org.kuali.rice.kim.bo.ui.PersonDocumentName; 036 import org.kuali.rice.kim.bo.ui.PersonDocumentRole; 037 import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember; 038 import org.kuali.rice.kim.document.IdentityManagementPersonDocument; 039 import org.kuali.rice.kim.document.authorization.IdentityManagementKimDocumentAuthorizer; 040 import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator; 041 import org.kuali.rice.kim.framework.type.KimTypeService; 042 import org.kuali.rice.kim.impl.KIMPropertyConstants; 043 import org.kuali.rice.kim.impl.identity.principal.PrincipalBo; 044 import org.kuali.rice.kim.impl.role.RoleMemberBo; 045 import org.kuali.rice.kim.impl.type.KimTypeBo; 046 import org.kuali.rice.kim.rule.event.ui.AddGroupEvent; 047 import org.kuali.rice.kim.rule.event.ui.AddPersonDelegationMemberEvent; 048 import org.kuali.rice.kim.rule.event.ui.AddRoleEvent; 049 import org.kuali.rice.kim.rule.ui.AddGroupRule; 050 import org.kuali.rice.kim.rule.ui.AddPersonDelegationMemberRule; 051 import org.kuali.rice.kim.rule.ui.AddPersonDocumentRoleQualifierRule; 052 import org.kuali.rice.kim.rule.ui.AddRoleRule; 053 import org.kuali.rice.kim.rules.ui.PersonDocumentDelegationMemberRule; 054 import org.kuali.rice.kim.rules.ui.PersonDocumentGroupRule; 055 import org.kuali.rice.kim.rules.ui.PersonDocumentRoleRule; 056 import org.kuali.rice.kim.service.KIMServiceLocatorInternal; 057 import org.kuali.rice.kim.service.UiDocumentService; 058 import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper; 059 import org.kuali.rice.krad.document.Document; 060 import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase; 061 import org.kuali.rice.krad.service.BusinessObjectService; 062 import org.kuali.rice.krad.service.KRADServiceLocator; 063 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 064 import org.kuali.rice.krad.util.GlobalVariables; 065 import org.kuali.rice.krad.util.KRADConstants; 066 import org.kuali.rice.krad.util.ObjectUtils; 067 068 import java.sql.Timestamp; 069 import java.util.ArrayList; 070 import java.util.HashMap; 071 import java.util.HashSet; 072 import java.util.List; 073 import java.util.Map; 074 import java.util.Set; 075 076 /** 077 * This is a description of what this class does - shyu don't forget to fill this in. 078 * 079 * @author Kuali Rice Team (rice.collab@kuali.org) 080 * 081 */ 082 public class IdentityManagementPersonDocumentRule extends TransactionalDocumentRuleBase implements AddGroupRule, AddRoleRule, AddPersonDocumentRoleQualifierRule, AddPersonDelegationMemberRule { 083 084 // protected static final Logger LOG = Logger.getLogger( IdentityManagementPersonDocumentRule.class ); 085 086 protected AddGroupRule addGroupRule; 087 protected AddRoleRule addRoleRule; 088 protected AddPersonDelegationMemberRule addPersonDelegationMemberRule; 089 protected IdentityManagementKimDocumentAuthorizer authorizer; 090 protected BusinessObjectService businessObjectService; 091 protected IdentityService identityService; 092 protected RoleService roleService; 093 protected UiDocumentService uiDocumentService; 094 protected Class<? extends AddGroupRule> addGroupRuleClass = PersonDocumentGroupRule.class; 095 protected Class<? extends AddRoleRule> addRoleRuleClass = PersonDocumentRoleRule.class; 096 protected Class<? extends AddPersonDelegationMemberRule> addPersonDelegationMemberRuleClass = PersonDocumentDelegationMemberRule.class; 097 098 protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper(); 099 100 @Override 101 protected boolean processCustomSaveDocumentBusinessRules(Document document) { 102 if (!(document instanceof IdentityManagementPersonDocument)) { 103 return false; 104 } 105 106 IdentityManagementPersonDocument personDoc = (IdentityManagementPersonDocument)document; 107 boolean valid = true; 108 109 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 110 111 //KRADServiceLocatorInternal.getDictionaryValidationService().validateDocument(document); 112 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false); 113 valid &= validDuplicatePrincipalName(personDoc); 114 EntityDefault origEntity = getIdentityService().getEntityDefault(personDoc.getEntityId()); 115 boolean isCreatingNew = origEntity==null?true:false; 116 if(getUIDocumentService().canModifyEntity(GlobalVariables.getUserSession().getPrincipalId(), personDoc.getPrincipalId()) || isCreatingNew) { 117 valid &= validateEntityInformation(isCreatingNew, personDoc); 118 } 119 // kimtypeservice.validateAttributes is not working yet. 120 valid &= validateRoleQualifier (personDoc.getRoles()); 121 valid &= validateDelegationMemberRoleQualifier(personDoc.getDelegationMembers()); 122 if (StringUtils.isNotBlank(personDoc.getPrincipalName())) { 123 valid &= doesPrincipalNameExist (personDoc.getPrincipalName(), personDoc.getPrincipalId()); 124 } 125 126 valid &= validActiveDatesForRole (personDoc.getRoles()); 127 valid &= validActiveDatesForGroup (personDoc.getGroups()); 128 valid &= validActiveDatesForDelegations (personDoc.getDelegationMembers()); 129 130 131 // all failed at this point. 132 // valid &= checkUnassignableRoles(personDoc); 133 // valid &= checkUnpopulatableGroups(personDoc); 134 135 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 136 137 return valid; 138 } 139 140 protected boolean validateEntityInformation(boolean isCreatingNew, IdentityManagementPersonDocument personDoc){ 141 boolean valid = true; 142 boolean canOverridePrivacyPreferences = getUIDocumentService().canOverrideEntityPrivacyPreferences(GlobalVariables.getUserSession().getPrincipalId(), personDoc.getPrincipalId()); 143 valid &= checkMultipleDefault (personDoc.getAffiliations(), "affiliations"); 144 if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressName()) { 145 valid &= checkMultipleDefault (personDoc.getNames(), "names"); 146 } 147 if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressAddress()) { 148 valid &= checkMultipleDefault (personDoc.getAddrs(), "addrs"); 149 } 150 if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressPhone()) { 151 valid &= checkMultipleDefault (personDoc.getPhones(), "phones"); 152 } 153 if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressEmail()) { 154 valid &= checkMultipleDefault (personDoc.getEmails(), "emails"); 155 } 156 valid &= checkPrimaryEmploymentInfo (personDoc.getAffiliations()); 157 valid &= validEmployeeIDForAffiliation(personDoc.getAffiliations()); 158 valid &= checkAffiliationTypeChange (personDoc.getAffiliations()); 159 valid &= checkUniqueAffiliationTypePerCampus(personDoc.getAffiliations()); 160 return valid; 161 } 162 163 @SuppressWarnings("unchecked") 164 protected boolean validDuplicatePrincipalName(IdentityManagementPersonDocument personDoc){ 165 Map<String, String> criteria = new HashMap<String, String>(); 166 criteria.put("principalName", personDoc.getPrincipalName()); 167 List<PrincipalBo> prncplImpls = (List<PrincipalBo>)getBusinessObjectService().findMatching(PrincipalBo.class, criteria); 168 boolean rulePassed = true; 169 if(prncplImpls!=null && prncplImpls.size()>0){ 170 if(prncplImpls.size()==1 && prncplImpls.get(0).getPrincipalId().equals(personDoc.getPrincipalId())) { 171 rulePassed = true; 172 } 173 else{ 174 GlobalVariables.getMessageMap().putError("document.principalName", 175 RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Principal Name"}); 176 rulePassed = false; 177 } 178 } 179 return rulePassed; 180 } 181 182 protected boolean checkUnassignableRoles(IdentityManagementPersonDocument document) { 183 boolean valid = true; 184 Map<String,Set<String>> unassignableRoles = getAuthorizer( document ).getUnassignableRoles(document, GlobalVariables.getUserSession().getPerson()); 185 for (String namespaceCode : unassignableRoles.keySet()) { 186 for (String roleName : unassignableRoles.get(namespaceCode)) { 187 int i = 0; 188 for (PersonDocumentRole role : document.getRoles()) { 189 if (role.isEditable() && namespaceCode.endsWith(role.getNamespaceCode()) && roleName.equals(role.getRoleName())) { 190 GlobalVariables.getMessageMap().putError("roles["+i+"].roleId", RiceKeyConstants.ERROR_ASSIGN_ROLE, new String[] {namespaceCode, roleName}); 191 valid = false; 192 } 193 i++; 194 } 195 } 196 } 197 return valid; 198 } 199 200 protected boolean checkUnpopulatableGroups(IdentityManagementPersonDocument document) { 201 boolean valid = true; 202 Map<String,Set<String>> unpopulatableGroups = getAuthorizer( document ).getUnpopulateableGroups(document, GlobalVariables.getUserSession().getPerson()); 203 for (String namespaceCode : unpopulatableGroups.keySet()) { 204 for (String groupName : unpopulatableGroups.get(namespaceCode)) { 205 int i = 0; 206 for (PersonDocumentGroup group : document.getGroups()) { 207 if ( (group.getNamespaceCode() != null && namespaceCode.endsWith(group.getNamespaceCode())) && (group.getGroupName() != null && groupName.equals(group.getGroupName()))) { 208 GlobalVariables.getMessageMap().putError("groups["+i+"].groupId", RiceKeyConstants.ERROR_POPULATE_GROUP, new String[] {namespaceCode, groupName}); 209 } 210 i++; 211 } 212 } 213 valid = false; 214 } 215 return valid; 216 } 217 218 @Override 219 protected boolean processCustomRouteDocumentBusinessRules(Document document) { 220 super.processCustomRouteDocumentBusinessRules(document); 221 IdentityManagementPersonDocument personDoc = (IdentityManagementPersonDocument)document; 222 boolean valid = true; 223 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 224 valid &= validateAffiliationAndName( personDoc ); 225 valid &= checkAffiliationEithOneEMpInfo (personDoc.getAffiliations()); 226 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 227 228 return valid; 229 } 230 231 232 protected boolean checkMultipleDefault (List <? extends PersonDocumentBoDefaultBase> boList, String listName) { 233 boolean valid = true; 234 boolean isDefaultSet = false; 235 int i = 0; 236 for (PersonDocumentBoDefaultBase item : boList) { 237 if (item.isDflt()) { 238 if (isDefaultSet) { 239 GlobalVariables.getMessageMap().putError(listName+"[" + i + "].dflt",RiceKeyConstants.ERROR_MULTIPLE_DEFAULT_SELETION); 240 valid = false; 241 } else { 242 isDefaultSet = true; 243 } 244 } 245 i++; 246 } 247 if (!boList.isEmpty() && !isDefaultSet) { 248 GlobalVariables.getMessageMap().putError(listName+"[0].dflt",RiceKeyConstants.ERROR_NO_DEFAULT_SELETION); 249 } 250 return valid; 251 } 252 253 protected boolean checkPrimaryEmploymentInfo (List <PersonDocumentAffiliation> affiliations) { 254 boolean valid = true; 255 int i = 0; 256 int firstAfflnCounter = -1; 257 boolean isPrimarySet = false; 258 for (PersonDocumentAffiliation affiliation : affiliations) { 259 int j = 0; 260 for (PersonDocumentEmploymentInfo empInfo : affiliation.getEmpInfos()) { 261 if(firstAfflnCounter==-1) { 262 firstAfflnCounter = i; 263 } 264 if (empInfo.isPrimary()) { 265 if (isPrimarySet) { 266 // primary per principal or primary per affiliation ? 267 GlobalVariables.getMessageMap().putError("affiliations[" + i + "].empInfos["+ j +"].primary",RiceKeyConstants.ERROR_MULTIPLE_PRIMARY_EMPLOYMENT); 268 valid = false; 269 } else { 270 isPrimarySet = true; 271 } 272 j++; 273 } 274 } 275 i++; 276 } 277 if(!isPrimarySet && firstAfflnCounter!=-1){ 278 GlobalVariables.getMessageMap().putError("affiliations[" + firstAfflnCounter + "].empInfos[0].primary",RiceKeyConstants.ERROR_NO_PRIMARY_EMPLOYMENT); 279 valid = false; 280 } 281 return valid; 282 } 283 284 protected boolean checkAffiliationTypeChange (List <PersonDocumentAffiliation> affiliations) { 285 boolean valid = true; 286 int i = 0; 287 for (PersonDocumentAffiliation affiliation : affiliations) { 288 if (affiliation.getAffiliationType() != null && !affiliation.getAffiliationTypeCode().equals(affiliation.getAffiliationType().getCode())) { 289 PersonDocumentAffiliation copiedAffiliation = (PersonDocumentAffiliation)ObjectUtils.deepCopy(affiliation); 290 copiedAffiliation.refreshReferenceObject("affiliationType"); 291 if (!copiedAffiliation.getAffiliationType().isEmploymentAffiliationType() && affiliation.getAffiliationType().isEmploymentAffiliationType() && !copiedAffiliation.getEmpInfos().isEmpty()) { 292 GlobalVariables.getMessageMap().putError("affiliations[" + i + "].affiliationTypeCode",RiceKeyConstants.ERROR_NOT_EMPLOYMENT_AFFILIATION_TYPE,new String[] {affiliation.getAffiliationType().getName(), copiedAffiliation.getAffiliationType().getName()}); 293 valid = false; 294 } 295 } 296 i++; 297 } 298 return valid; 299 } 300 301 protected boolean validEmployeeIDForAffiliation(List <PersonDocumentAffiliation> affiliations) { 302 boolean valid = true; 303 int i = 0; 304 int j = 0; 305 for(PersonDocumentAffiliation affiliation : affiliations) { 306 if(affiliation.getAffiliationType() != null && affiliation.getAffiliationType().isEmploymentAffiliationType()){ 307 if(affiliation.getEmpInfos()!=null){ 308 j = 0; 309 for (PersonDocumentEmploymentInfo empInfo : affiliation.getEmpInfos()) { 310 if (StringUtils.isEmpty(empInfo.getEmployeeId())) { 311 GlobalVariables.getMessageMap().putError("affiliations[" + i + "].empInfos["+ j +"].employeeId", RiceKeyConstants.ERROR_REQUIRED_CONDITIONALLY, new String[] {"Employee ID", "an employee"}); 312 valid = false; 313 j++; 314 } 315 } 316 } 317 } 318 i++; 319 } 320 return valid; 321 } 322 323 protected boolean isPersonAnEmployee(List<PersonDocumentAffiliation> affiliations){ 324 boolean isEmployee = false; 325 for (PersonDocumentAffiliation affiliation : affiliations){ 326 if (affiliation.getAffiliationType() != null && affiliation.getAffiliationType().isEmploymentAffiliationType()){ 327 isEmployee = true; 328 break; 329 } 330 } 331 return isEmployee; 332 } 333 334 protected boolean checkUniqueAffiliationTypePerCampus (List <PersonDocumentAffiliation> affiliations) { 335 boolean valid = true; 336 int i = 0; 337 for (PersonDocumentAffiliation affiliation : affiliations) { 338 int j = 0; 339 for (PersonDocumentAffiliation affiliation1 : affiliations) { 340 if (j > i && affiliation.getAffiliationTypeCode() .equals(affiliation1.getAffiliationTypeCode()) && affiliation.getCampusCode().equals(affiliation1.getCampusCode())) { 341 GlobalVariables.getMessageMap().putError("affiliations[" + j + "].affiliationTypeCode",RiceKeyConstants.ERROR_NOT_UNIQUE_AFFILIATION_TYPE_PER_CAMPUE, affiliation.getAffiliationType().getName()); 342 valid = false; 343 } 344 j++; 345 } 346 i++; 347 } 348 return valid; 349 } 350 351 protected boolean checkAffiliationEithOneEMpInfo (List <PersonDocumentAffiliation> affiliations) { 352 boolean valid = true; 353 int i = 0; 354 for (PersonDocumentAffiliation affiliation : affiliations) { 355 if (affiliation.getAffiliationType() .isEmploymentAffiliationType() && affiliation.getEmpInfos().isEmpty()) { 356 GlobalVariables.getMessageMap().putError("affiliations[" + i + "].affiliationTypeCode",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "Employment Information"); 357 valid = false; 358 } 359 i++; 360 } 361 return valid; 362 } 363 364 /* 365 * Verify at least one affiliation and one default name 366 */ 367 protected boolean validateAffiliationAndName(IdentityManagementPersonDocument personDoc) { 368 boolean valid = true; 369 if (personDoc.getAffiliations().isEmpty()) { 370 GlobalVariables.getMessageMap().putError("affiliations[0]",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "affiliation"); 371 valid = false; 372 } 373 if (personDoc.getNames().isEmpty()) { 374 GlobalVariables.getMessageMap().putError("names[0]",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "name"); 375 valid = false; 376 } else{ 377 boolean activeExists = false; 378 for(PersonDocumentName name: personDoc.getNames()){ 379 if(name.isActive()){ 380 activeExists = true; 381 } 382 } 383 if(!activeExists){ 384 GlobalVariables.getMessageMap().putError("names[0]", RiceKeyConstants.ERROR_ONE_ACTIVE_ITEM_REQUIRED, "name"); 385 valid = false; 386 } 387 return valid; 388 389 } 390 return valid; 391 } 392 393 protected boolean doesPrincipalNameExist (String principalName, String principalId) { 394 Principal principal = getIdentityService().getPrincipalByPrincipalName(principalName); 395 if (principal != null && (StringUtils.isBlank(principalId) || !principal.getPrincipalId().equals(principalId))) { 396 GlobalVariables.getMessageMap().putError(KIMPropertyConstants.Person.PRINCIPAL_NAME,RiceKeyConstants.ERROR_EXIST_PRINCIPAL_NAME, principalName); 397 return false; 398 } 399 return true; 400 } 401 402 protected boolean validateRoleQualifier( List<PersonDocumentRole> roles ) { 403 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 404 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 405 int i = 0; 406 for(PersonDocumentRole role : roles ) { 407 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to( 408 role.getKimRoleType())); 409 if(CollectionUtils.isEmpty(role.getRolePrncpls()) && !role.getDefinitions().isEmpty()){ 410 KimType kimTypeInfo = KimApiServiceLocator.getKimTypeInfoService().getKimType(role.getKimRoleType().getId()); 411 Map<String, String> blankQualifiers = attributeValidationHelper.getBlankValueQualifiersMap(kimTypeInfo.getAttributeDefinitions()); 412 List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( 413 role.getKimRoleType().getId(), blankQualifiers); 414 if(localErrors!=null && !localErrors.isEmpty()){ 415 GlobalVariables.getMessageMap().putError("document.roles["+i+"].newRolePrncpl.qualifiers[0].attrVal", 416 RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "Role Qualifier"); 417 return false; 418 } 419 } 420 421 final List<KimAttributeField> attributeDefinitions = role.getDefinitions(); 422 final Set<String> uniqueQualifierAttributes = findUniqueQualificationAttributes(role, attributeDefinitions); 423 424 if ( kimTypeService != null ) { 425 int j = 0; 426 for ( KimDocumentRoleMember rolePrincipal : role.getRolePrncpls() ) { 427 List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), attributeValidationHelper.convertQualifiersToMap( rolePrincipal.getQualifiers() ) ); 428 validationErrors.addAll( attributeValidationHelper.convertErrors( 429 "roles[" + i + "].rolePrncpls[" + j + "]", 430 attributeValidationHelper.convertQualifiersToAttrIdxMap(rolePrincipal.getQualifiers()), 431 localErrors)); 432 433 if (uniqueQualifierAttributes.size() > 0) { 434 validateUniquePersonRoleQualifiersUniqueForMembership(role, rolePrincipal, j, uniqueQualifierAttributes, i, validationErrors); 435 } 436 437 j++; 438 } 439 } 440 i++; 441 } 442 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 443 if (validationErrors.isEmpty()) { 444 return true; 445 } else { 446 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 447 return false; 448 } 449 } 450 451 /** 452 * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique 453 * 454 * @param roleIndex the index of the role on the document (for error reporting purposes) 455 * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes) 456 * @return true if all unique values are indeed unique, false otherwise 457 */ 458 protected boolean validateUniquePersonRoleQualifiersUniqueForMembership(PersonDocumentRole role, KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, Set<String> uniqueQualifierAttributes, int roleIndex, List<RemotableAttributeError> validationErrors) { 459 boolean foundError = false; 460 int count = 0; 461 462 for (KimDocumentRoleMember membership : role.getRolePrncpls()) { 463 if (membershipToCheckIndex != count) { 464 if (sameMembershipQualifications(membershipToCheck, membership, uniqueQualifierAttributes)) { 465 foundError = true; 466 467 int qualifierCount = 0; 468 469 for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) { 470 if (qualifier != null && uniqueQualifierAttributes.contains(qualifier.getKimAttrDefnId())) { 471 validationErrors.add(RemotableAttributeError.Builder.create("document.roles["+roleIndex+"].rolePrncpls["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+qualifier.getKimAttribute().getAttributeName()+";"+qualifier.getAttrVal()).build()); 472 } 473 qualifierCount += 1; 474 } 475 } 476 } 477 478 count += 1; 479 } 480 return foundError; 481 } 482 483 /** 484 * Determines if two seperate memberships have the same qualifications 485 * @param membershipA the first membership to check 486 * @param membershipB the second membership to check 487 * @param uniqueQualifierAttributes the set of qualifier attributes which need to be unique 488 * @return true if equal, false if otherwise 489 */ 490 protected boolean sameMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueQualifierAttributes) { 491 boolean equalSoFar = true; 492 for (String uniqueQualifierAttributeDefinitionId : uniqueQualifierAttributes) { 493 final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(uniqueQualifierAttributeDefinitionId); 494 final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(uniqueQualifierAttributeDefinitionId); 495 496 if (qualifierA != null && qualifierB != null) { 497 equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal())); 498 } 499 } 500 return equalSoFar; 501 } 502 503 /** 504 * Finds the set of unique qualification attributes for the given role 505 * 506 * @param role the role associated with this person 507 * @param attributeDefinitions the Map of attribute definitions where we can find out if a KimAttribute is supposed to be unique 508 * @return a Set of attribute definition ids for qualifications which are supposed to be unique 509 */ 510 public Set<String> findUniqueQualificationAttributes(PersonDocumentRole role, List<KimAttributeField> attributeDefinitions) { 511 Set<String> uniqueQualifications = new HashSet<String>(); 512 513 if (role.getRolePrncpls() != null && role.getRolePrncpls().size() > 1) { 514 final KimDocumentRoleMember membership = role.getRolePrncpls().get(0); 515 for (KimDocumentRoleQualifier qualifier: membership.getQualifiers()) { 516 if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) { 517 final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField( 518 qualifier.getKimAttribute().getAttributeName(), attributeDefinitions); 519 520 if (relatedDefinition != null && relatedDefinition.isUnique()) { 521 uniqueQualifications.add(qualifier.getKimAttrDefnId()); 522 } 523 } 524 } 525 } 526 527 return uniqueQualifications; 528 } 529 530 protected boolean validActiveDatesForRole (List<PersonDocumentRole> roles ) { 531 boolean valid = true; 532 int i = 0; 533 for(PersonDocumentRole role : roles ) { 534 int j = 0; 535 for (KimDocumentRoleMember principal : role.getRolePrncpls()) { 536 valid &= validateActiveDate("roles["+i+"].rolePrncpls["+j+"].activeToDate",principal.getActiveFromDate(), principal.getActiveToDate()); 537 j++; 538 } 539 i++; 540 } 541 return valid; 542 } 543 544 protected boolean validActiveDatesForGroup (List<PersonDocumentGroup> groups ) { 545 boolean valid = true; 546 int i = 0; 547 for(PersonDocumentGroup group : groups ) { 548 valid &= validateActiveDate("groups["+i+"].activeToDate",group.getActiveFromDate(), group.getActiveToDate()); 549 i++; 550 } 551 return valid; 552 } 553 554 protected boolean validActiveDatesForDelegations(List<RoleDocumentDelegationMember> delegationMembers) { 555 boolean valid = true; 556 int i = 0; 557 for(RoleDocumentDelegationMember delegationMember: delegationMembers){ 558 valid &= validateActiveDate("delegationMembers["+i+"].activeToDate", delegationMember.getActiveFromDate(), delegationMember.getActiveToDate()); 559 i++; 560 } 561 return valid; 562 } 563 564 protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) { 565 // TODO : do not have detail bus rule yet, so just check this for now. 566 boolean valid = true; 567 if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) { 568 GlobalVariables.getMessageMap().putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE); 569 valid = false; 570 } 571 return valid; 572 } 573 574 public boolean processAddGroup(AddGroupEvent addGroupEvent) { 575 return getAddGroupRule().processAddGroup(addGroupEvent); 576 } 577 578 public boolean processAddRole(AddRoleEvent addRoleEvent) { 579 return getAddRoleRule().processAddRole(addRoleEvent); 580 } 581 582 public boolean processAddPersonDelegationMember(AddPersonDelegationMemberEvent addPersonDelegationMemberEvent){ 583 return getAddPersonDelegationMemberRule().processAddPersonDelegationMember(addPersonDelegationMemberEvent); 584 } 585 586 public IdentityService getIdentityService() { 587 if ( identityService == null ) { 588 identityService = KimApiServiceLocator.getIdentityService(); 589 } 590 return identityService; 591 } 592 593 public RoleService getRoleService() { 594 if ( roleService == null ) { 595 roleService = KimApiServiceLocator.getRoleService(); 596 } 597 return roleService; 598 } 599 600 public UiDocumentService getUIDocumentService() { 601 if ( uiDocumentService == null ) { 602 uiDocumentService = KIMServiceLocatorInternal.getUiDocumentService(); 603 } 604 return uiDocumentService; 605 } 606 607 public IdentityManagementKimDocumentAuthorizer getAuthorizer(IdentityManagementPersonDocument document) { 608 if ( authorizer == null ) { 609 authorizer = (IdentityManagementKimDocumentAuthorizer) KRADServiceLocatorWeb.getDocumentDictionaryService().getDocumentAuthorizer(document); 610 } 611 return authorizer; 612 } 613 614 615 616 /** 617 * @return the addGroupRuleClass 618 */ 619 public Class<? extends AddGroupRule> getAddGroupRuleClass() { 620 return this.addGroupRuleClass; 621 } 622 623 624 625 /** 626 * Can be overridden by subclasses to indicate the rule class to use when adding groups. 627 * 628 * @param addGroupRuleClass the addGroupRuleClass to set 629 */ 630 public void setAddGroupRuleClass(Class<? extends AddGroupRule> addGroupRuleClass) { 631 this.addGroupRuleClass = addGroupRuleClass; 632 } 633 634 635 636 /** 637 * @return the addRoleRuleClass 638 */ 639 public Class<? extends AddRoleRule> getAddRoleRuleClass() { 640 return this.addRoleRuleClass; 641 } 642 643 644 645 /** 646 * Can be overridden by subclasses to indicate the rule class to use when adding roles. 647 * 648 * @param addRoleRuleClass the addRoleRuleClass to set 649 */ 650 public void setAddRoleRuleClass(Class<? extends AddRoleRule> addRoleRuleClass) { 651 this.addRoleRuleClass = addRoleRuleClass; 652 } 653 654 655 656 /** 657 * @return the addGroupRule 658 */ 659 public AddGroupRule getAddGroupRule() { 660 if ( addGroupRule == null ) { 661 try { 662 addGroupRule = addGroupRuleClass.newInstance(); 663 } catch ( Exception ex ) { 664 throw new RuntimeException( "Unable to create AddGroupRule instance using class: " + addGroupRuleClass, ex ); 665 } 666 } 667 return addGroupRule; 668 } 669 670 671 672 /** 673 * @return the addRoleRule 674 */ 675 public AddRoleRule getAddRoleRule() { 676 if ( addRoleRule == null ) { 677 try { 678 addRoleRule = addRoleRuleClass.newInstance(); 679 } catch ( Exception ex ) { 680 throw new RuntimeException( "Unable to create AddRoleRule instance using class: " + addRoleRuleClass, ex ); 681 } 682 } 683 return addRoleRule; 684 } 685 686 /** 687 * @return the addRoleRule 688 */ 689 public AddPersonDelegationMemberRule getAddPersonDelegationMemberRule() { 690 if(addPersonDelegationMemberRule == null){ 691 try { 692 addPersonDelegationMemberRule = addPersonDelegationMemberRuleClass.newInstance(); 693 } catch ( Exception ex ) { 694 throw new RuntimeException( "Unable to create AddPersonDelegationMemberRuleClass instance using class: " + addPersonDelegationMemberRuleClass, ex ); 695 } 696 } 697 return addPersonDelegationMemberRule; 698 } 699 700 /** 701 * @return the businessObjectService 702 */ 703 public BusinessObjectService getBusinessObjectService() { 704 if ( businessObjectService == null ) { 705 businessObjectService = KRADServiceLocator.getBusinessObjectService(); 706 } 707 return businessObjectService; 708 } 709 710 public boolean processAddPersonDocumentRoleQualifier(IdentityManagementPersonDocument document, PersonDocumentRole role, KimDocumentRoleMember kimDocumentRoleMember, int selectedRoleIdx) { 711 boolean dateValidationSuccess = validateActiveDate("document.roles[" + selectedRoleIdx + "].newRolePrncpl.activeFromDate", kimDocumentRoleMember.getActiveFromDate(), kimDocumentRoleMember.getActiveToDate()); 712 String errorPath = "roles[" + selectedRoleIdx + "].newRolePrncpl"; 713 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 714 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 715 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to(role.getKimRoleType())); 716 717 List<RemotableAttributeError> errorsAttributesAgainstExisting; 718 int i = 0; 719 boolean rulePassed = true; 720 Map<String, String> newMemberQualifiers = attributeValidationHelper.convertQualifiersToMap(kimDocumentRoleMember.getQualifiers()); 721 Map<String, String> oldMemberQualifiers; 722 List<String> roleIds = new ArrayList<String>(); 723 roleIds.add(role.getRoleId()); 724 for(KimDocumentRoleMember member: role.getRolePrncpls()){ 725 oldMemberQualifiers = member.getQualifierAsMap(); 726 errorsAttributesAgainstExisting = kimTypeService.validateUniqueAttributes( 727 role.getKimRoleType().getId(), newMemberQualifiers, oldMemberQualifiers); 728 validationErrors.addAll( 729 attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper 730 .convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()), 731 errorsAttributesAgainstExisting)); 732 i++; 733 } 734 735 if ( kimTypeService != null ) { 736 List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), newMemberQualifiers ); 737 validationErrors.addAll( attributeValidationHelper.convertErrors(errorPath, 738 attributeValidationHelper.convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()), 739 localErrors)); 740 } 741 742 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 743 if (validationErrors.isEmpty()) { 744 rulePassed = dateValidationSuccess; 745 } else { 746 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 747 rulePassed = false; 748 } 749 return rulePassed; 750 } 751 752 protected boolean validateDelegationMemberRoleQualifier(List<RoleDocumentDelegationMember> delegationMembers){ 753 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 754 boolean valid; 755 int memberCounter = 0; 756 List<RemotableAttributeError> errorsTemp; 757 Map<String, String> mapToValidate; 758 KimTypeService kimTypeService; 759 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 760 RoleMemberBo roleMember; 761 String errorPath; 762 ArrayList<String> roleIds; 763 KimType kimType; 764 for(RoleDocumentDelegationMember delegationMember: delegationMembers) { 765 kimType = KimTypeBo.to(delegationMember.getRoleBo().getKimRoleType()); 766 kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType); 767 roleIds = new ArrayList<String>(); 768 roleIds.add(delegationMember.getRoleBo().getId()); 769 errorPath = "delegationMembers["+memberCounter+"]"; 770 mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers()); 771 errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate); 772 validationErrors.addAll( 773 attributeValidationHelper.convertErrors(errorPath, 774 attributeValidationHelper.convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()), 775 errorsTemp)); 776 777 roleMember = getUIDocumentService().getRoleMember(delegationMember.getRoleMemberId()); 778 if(roleMember==null){ 779 valid = false; 780 GlobalVariables.getMessageMap().putError("document."+errorPath, RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{}); 781 } else{ 782 kimTypeService.validateUnmodifiableAttributes(kimType.getId(), roleMember.getAttributes(), mapToValidate); 783 validationErrors.addAll( 784 attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper 785 .convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()), errorsTemp)); 786 } 787 memberCounter++; 788 } 789 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 790 if (validationErrors.isEmpty()) { 791 valid = true; 792 } else { 793 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 794 valid = false; 795 } 796 return valid; 797 } 798 799 }