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