View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.document.rule;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.uif.RemotableAttributeError;
21  import org.kuali.rice.core.api.util.RiceKeyConstants;
22  import org.kuali.rice.kim.api.identity.IdentityService;
23  import org.kuali.rice.kim.api.identity.entity.EntityDefault;
24  import org.kuali.rice.kim.api.identity.principal.Principal;
25  import org.kuali.rice.kim.api.role.RoleService;
26  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
27  import org.kuali.rice.kim.api.type.KimAttributeField;
28  import org.kuali.rice.kim.api.type.KimType;
29  import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
30  import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
31  import org.kuali.rice.kim.bo.ui.PersonDocumentAffiliation;
32  import org.kuali.rice.kim.bo.ui.PersonDocumentBoDefaultBase;
33  import org.kuali.rice.kim.bo.ui.PersonDocumentEmploymentInfo;
34  import org.kuali.rice.kim.bo.ui.PersonDocumentGroup;
35  import org.kuali.rice.kim.bo.ui.PersonDocumentName;
36  import org.kuali.rice.kim.bo.ui.PersonDocumentRole;
37  import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
38  import org.kuali.rice.kim.document.IdentityManagementPersonDocument;
39  import org.kuali.rice.kim.document.authorization.IdentityManagementKimDocumentAuthorizer;
40  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
41  import org.kuali.rice.kim.framework.type.KimTypeService;
42  import org.kuali.rice.kim.impl.KIMPropertyConstants;
43  import org.kuali.rice.kim.impl.identity.principal.PrincipalBo;
44  import org.kuali.rice.kim.impl.role.RoleMemberBo;
45  import org.kuali.rice.kim.impl.type.KimTypeBo;
46  import org.kuali.rice.kim.rule.event.ui.AddGroupEvent;
47  import org.kuali.rice.kim.rule.event.ui.AddPersonDelegationMemberEvent;
48  import org.kuali.rice.kim.rule.event.ui.AddRoleEvent;
49  import org.kuali.rice.kim.rule.ui.AddGroupRule;
50  import org.kuali.rice.kim.rule.ui.AddPersonDelegationMemberRule;
51  import org.kuali.rice.kim.rule.ui.AddPersonDocumentRoleQualifierRule;
52  import org.kuali.rice.kim.rule.ui.AddRoleRule;
53  import org.kuali.rice.kim.rules.ui.PersonDocumentDelegationMemberRule;
54  import org.kuali.rice.kim.rules.ui.PersonDocumentGroupRule;
55  import org.kuali.rice.kim.rules.ui.PersonDocumentRoleRule;
56  import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
57  import org.kuali.rice.kim.service.UiDocumentService;
58  import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
59  import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
60  import org.kuali.rice.krad.document.Document;
61  import org.kuali.rice.krad.service.BusinessObjectService;
62  import org.kuali.rice.krad.service.KRADServiceLocator;
63  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
64  import org.kuali.rice.krad.util.GlobalVariables;
65  import org.kuali.rice.krad.util.KRADConstants;
66  import org.kuali.rice.krad.util.ObjectUtils;
67  
68  import java.sql.Timestamp;
69  import java.util.ArrayList;
70  import java.util.HashMap;
71  import java.util.HashSet;
72  import java.util.List;
73  import java.util.Map;
74  import java.util.Set;
75  
76  /**
77   * This is a description of what this class does - shyu don't forget to fill this in.
78   *
79   * @author Kuali Rice Team (rice.collab@kuali.org)
80   *
81   */
82  public class IdentityManagementPersonDocumentRule extends TransactionalDocumentRuleBase implements AddGroupRule, AddRoleRule, AddPersonDocumentRoleQualifierRule, AddPersonDelegationMemberRule {
83  
84  //	protected static final Logger LOG = Logger.getLogger( IdentityManagementPersonDocumentRule.class );
85  
86  	protected AddGroupRule addGroupRule;
87  	protected AddRoleRule  addRoleRule;
88  	protected AddPersonDelegationMemberRule addPersonDelegationMemberRule;
89  	protected IdentityManagementKimDocumentAuthorizer authorizer;
90  	protected BusinessObjectService businessObjectService;
91  	protected IdentityService identityService;
92  	protected RoleService roleService;
93  	protected UiDocumentService uiDocumentService;
94  	protected Class<? extends AddGroupRule> addGroupRuleClass = PersonDocumentGroupRule.class;
95  	protected Class<? extends AddRoleRule> addRoleRuleClass = PersonDocumentRoleRule.class;
96  	protected Class<? extends AddPersonDelegationMemberRule> addPersonDelegationMemberRuleClass = PersonDocumentDelegationMemberRule.class;
97      protected ActiveRoleMemberHelper activeRoleMemberHelper = new ActiveRoleMemberHelper();
98  	protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper();
99  
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 
427 	        	for ( KimDocumentRoleMember rolePrincipal : activeRoleMemberHelper.getActiveRoleMembers(role.getRolePrncpls()) ) {
428 	        		List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), attributeValidationHelper.convertQualifiersToMap( rolePrincipal.getQualifiers() ) );
429 			        validationErrors.addAll( attributeValidationHelper.convertErrors(
430                             "roles[" + i + "].rolePrncpls[" + j + "]",
431                             attributeValidationHelper.convertQualifiersToAttrIdxMap(rolePrincipal.getQualifiers()),
432                             localErrors));
433 
434 			        if (uniqueQualifierAttributes.size() > 0) {
435 			        	validateUniquePersonRoleQualifiersUniqueForMembership(role, rolePrincipal, j, uniqueQualifierAttributes, i, validationErrors);
436 			        }
437 
438 			        j++;
439 		        }
440 	        }
441         	i++;
442     	}
443         GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
444     	if (validationErrors.isEmpty()) {
445     		return true;
446     	} else {
447     		attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
448     		return false;
449     	}
450     }
451 
452     /**
453      * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
454      *
455      * @param roleIndex the index of the role on the document (for error reporting purposes)
456      * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
457      * @return true if all unique values are indeed unique, false otherwise
458      */
459     protected boolean validateUniquePersonRoleQualifiersUniqueForMembership(PersonDocumentRole role, KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, Set<String> uniqueQualifierAttributes, int roleIndex, List<RemotableAttributeError> validationErrors) {
460     	boolean foundError = false;
461     	int count = 0;
462 
463     	for (KimDocumentRoleMember membership : role.getRolePrncpls()) {
464             if (sameMembershipQualifications(membershipToCheck, membership, uniqueQualifierAttributes)) {
465                 if (count == 0 ) {
466                     count +=1;
467                 } else {
468                     count += 1;
469                     foundError = true;
470 
471         		    int qualifierCount = 0;
472 
473 		    	    for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
474 			    	    if (qualifier != null && uniqueQualifierAttributes.contains(qualifier.getKimAttrDefnId())) {
475 						    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());
476 				   	    }
477 				        qualifierCount += 1;
478 				    }
479                 }
480     		}
481     	}
482     	return foundError;
483     }
484 
485     /**
486      * Determines if two seperate memberships have the same qualifications
487      * @param membershipA the first membership to check
488      * @param membershipB the second membership to check
489      * @param uniqueQualifierAttributes the set of qualifier attributes which need to be unique
490      * @return true if equal, false if otherwise
491      */
492     protected boolean sameMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueQualifierAttributes) {
493     	boolean equalSoFar = true;
494     	for (String uniqueQualifierAttributeDefinitionId : uniqueQualifierAttributes) {
495     		final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(uniqueQualifierAttributeDefinitionId);
496     		final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(uniqueQualifierAttributeDefinitionId);
497 
498     		if (qualifierA != null && qualifierB != null) {
499     			equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
500     		}
501     	}
502     	return equalSoFar;
503     }
504 
505     /**
506      * Finds the set of unique qualification attributes for the given role
507      *
508      * @param role the role associated with this person
509      * @param attributeDefinitions the Map of attribute definitions where we can find out if a KimAttribute is supposed to be unique
510      * @return a Set of attribute definition ids for qualifications which are supposed to be unique
511      */
512     public Set<String> findUniqueQualificationAttributes(PersonDocumentRole role, List<KimAttributeField> attributeDefinitions) {
513     	Set<String> uniqueQualifications = new HashSet<String>();
514 
515     	if (role.getRolePrncpls() != null && role.getRolePrncpls().size() > 1) {
516     		final KimDocumentRoleMember membership = role.getRolePrncpls().get(0);
517     		for (KimDocumentRoleQualifier qualifier: membership.getQualifiers()) {
518     			if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
519     	    		final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
520                             qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
521 
522     	    		if (relatedDefinition != null && relatedDefinition.isUnique()) {
523     	    			uniqueQualifications.add(qualifier.getKimAttrDefnId());
524     	    		}
525     			}
526     		}
527     	}
528 
529     	return uniqueQualifications;
530     }
531 
532     protected boolean validActiveDatesForRole (List<PersonDocumentRole> roles ) {
533     	boolean valid = true;
534 		int i = 0;
535     	for(PersonDocumentRole role : roles ) {
536 			int j = 0;
537     		for (KimDocumentRoleMember principal : role.getRolePrncpls()) {
538     			valid &= validateActiveDate("roles["+i+"].rolePrncpls["+j+"].activeToDate",principal.getActiveFromDate(), principal.getActiveToDate());
539     			j++;
540     		}
541     		i++;
542     	}
543     	return valid;
544     }
545 
546     protected boolean validActiveDatesForGroup (List<PersonDocumentGroup> groups ) {
547     	boolean valid = true;
548 		int i = 0;
549     	for(PersonDocumentGroup group : groups ) {
550      		valid &= validateActiveDate("groups["+i+"].activeToDate",group.getActiveFromDate(), group.getActiveToDate());
551     		i++;
552     	}
553     	return valid;
554     }
555 
556     protected boolean validActiveDatesForDelegations(List<RoleDocumentDelegationMember> delegationMembers) {
557     	boolean valid = true;
558 		int i = 0;
559 		for(RoleDocumentDelegationMember delegationMember: delegationMembers){
560      		valid &= validateActiveDate("delegationMembers["+i+"].activeToDate", delegationMember.getActiveFromDate(), delegationMember.getActiveToDate());
561     		i++;
562 		}
563     	return valid;
564     }
565 
566 	protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
567 		// TODO : do not have detail bus rule yet, so just check this for now.
568 		boolean valid = true;
569 		if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
570 	        GlobalVariables.getMessageMap().putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
571             valid = false;
572 		}
573 		return valid;
574 	}
575 
576     public boolean processAddGroup(AddGroupEvent addGroupEvent) {
577         return getAddGroupRule().processAddGroup(addGroupEvent);
578     }
579 
580     public boolean processAddRole(AddRoleEvent addRoleEvent) {
581         return getAddRoleRule().processAddRole(addRoleEvent);
582     }
583 
584     public boolean processAddPersonDelegationMember(AddPersonDelegationMemberEvent addPersonDelegationMemberEvent){
585     	return getAddPersonDelegationMemberRule().processAddPersonDelegationMember(addPersonDelegationMemberEvent);
586     }
587 
588 	public IdentityService getIdentityService() {
589 		if ( identityService == null ) {
590 			identityService = KimApiServiceLocator.getIdentityService();
591 		}
592 		return identityService;
593 	}
594 
595 	public RoleService getRoleService() {
596 		if ( roleService == null ) {
597 			roleService = KimApiServiceLocator.getRoleService();
598 		}
599 		return roleService;
600 	}
601 
602 	public UiDocumentService getUIDocumentService() {
603 		if ( uiDocumentService == null ) {
604 			uiDocumentService = KIMServiceLocatorInternal.getUiDocumentService();
605 		}
606 		return uiDocumentService;
607 	}
608 
609 	public IdentityManagementKimDocumentAuthorizer getAuthorizer(IdentityManagementPersonDocument document) {
610 		if ( authorizer == null ) {
611 			authorizer = (IdentityManagementKimDocumentAuthorizer) KRADServiceLocatorWeb.getDocumentDictionaryService().getDocumentAuthorizer(document);
612 		}
613 		return authorizer;
614 	}
615 
616 
617 
618 	/**
619 	 * @return the addGroupRuleClass
620 	 */
621 	public Class<? extends AddGroupRule> getAddGroupRuleClass() {
622 		return this.addGroupRuleClass;
623 	}
624 
625 
626 
627 	/**
628 	 * Can be overridden by subclasses to indicate the rule class to use when adding groups.
629 	 *
630 	 * @param addGroupRuleClass the addGroupRuleClass to set
631 	 */
632 	public void setAddGroupRuleClass(Class<? extends AddGroupRule> addGroupRuleClass) {
633 		this.addGroupRuleClass = addGroupRuleClass;
634 	}
635 
636 
637 
638 	/**
639 	 * @return the addRoleRuleClass
640 	 */
641 	public Class<? extends AddRoleRule> getAddRoleRuleClass() {
642 		return this.addRoleRuleClass;
643 	}
644 
645 
646 
647 	/**
648 	 * Can be overridden by subclasses to indicate the rule class to use when adding roles.
649 	 *
650 	 * @param addRoleRuleClass the addRoleRuleClass to set
651 	 */
652 	public void setAddRoleRuleClass(Class<? extends AddRoleRule> addRoleRuleClass) {
653 		this.addRoleRuleClass = addRoleRuleClass;
654 	}
655 
656 
657 
658 	/**
659 	 * @return the addGroupRule
660 	 */
661 	public AddGroupRule getAddGroupRule() {
662 		if ( addGroupRule == null ) {
663 			try {
664 				addGroupRule = addGroupRuleClass.newInstance();
665 			} catch ( Exception ex ) {
666 				throw new RuntimeException( "Unable to create AddGroupRule instance using class: " + addGroupRuleClass, ex );
667 			}
668 		}
669 		return addGroupRule;
670 	}
671 
672 
673 
674 	/**
675 	 * @return the addRoleRule
676 	 */
677 	public AddRoleRule getAddRoleRule() {
678 		if ( addRoleRule == null ) {
679 			try {
680 				addRoleRule = addRoleRuleClass.newInstance();
681 			} catch ( Exception ex ) {
682 				throw new RuntimeException( "Unable to create AddRoleRule instance using class: " + addRoleRuleClass, ex );
683 			}
684 		}
685 		return addRoleRule;
686 	}
687 
688 	/**
689 	 * @return the addRoleRule
690 	 */
691 	public AddPersonDelegationMemberRule getAddPersonDelegationMemberRule() {
692 		if(addPersonDelegationMemberRule == null){
693 			try {
694 				addPersonDelegationMemberRule = addPersonDelegationMemberRuleClass.newInstance();
695 			} catch ( Exception ex ) {
696 				throw new RuntimeException( "Unable to create AddPersonDelegationMemberRuleClass instance using class: " + addPersonDelegationMemberRuleClass, ex );
697 			}
698 		}
699 		return addPersonDelegationMemberRule;
700 	}
701 
702 	/**
703 	 * @return the businessObjectService
704 	 */
705 	public BusinessObjectService getBusinessObjectService() {
706 		if ( businessObjectService == null ) {
707 			businessObjectService = KRADServiceLocator.getBusinessObjectService();
708 		}
709 		return businessObjectService;
710 	}
711 
712 	public boolean processAddPersonDocumentRoleQualifier(IdentityManagementPersonDocument document, PersonDocumentRole role, KimDocumentRoleMember kimDocumentRoleMember, int selectedRoleIdx) {
713 		boolean dateValidationSuccess = validateActiveDate("document.roles[" + selectedRoleIdx + "].newRolePrncpl.activeFromDate", kimDocumentRoleMember.getActiveFromDate(), kimDocumentRoleMember.getActiveToDate());
714 		String errorPath = "roles[" + selectedRoleIdx + "].newRolePrncpl";
715 		List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
716         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
717         KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to(role.getKimRoleType()));
718 
719 		List<RemotableAttributeError> errorsAttributesAgainstExisting;
720 	    int i = 0;
721 	    boolean rulePassed = true;
722 	    Map<String, String> newMemberQualifiers = attributeValidationHelper.convertQualifiersToMap(kimDocumentRoleMember.getQualifiers());
723 	    Map<String, String> oldMemberQualifiers;
724 	    List<String> roleIds = new ArrayList<String>();
725 	    roleIds.add(role.getRoleId());
726 	    for(KimDocumentRoleMember member: role.getRolePrncpls()){
727 	    	oldMemberQualifiers = member.getQualifierAsMap();
728 	    	errorsAttributesAgainstExisting = kimTypeService.validateUniqueAttributes(
729 	    			role.getKimRoleType().getId(), newMemberQualifiers, oldMemberQualifiers);
730 	    	validationErrors.addAll(
731 					attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
732                             .convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
733                             errorsAttributesAgainstExisting));
734 	    	i++;
735 	    }
736 
737         if ( kimTypeService != null ) {
738         	List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), newMemberQualifiers );
739     	    validationErrors.addAll( attributeValidationHelper.convertErrors(errorPath,
740                     attributeValidationHelper.convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
741                     localErrors));
742         }
743 
744         GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
745     	if (validationErrors.isEmpty()) {
746     		rulePassed = dateValidationSuccess;
747     	} else {
748     		attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
749     		rulePassed = false;
750     	}
751     	return rulePassed;
752 	}
753 
754     protected boolean validateDelegationMemberRoleQualifier(List<RoleDocumentDelegationMember> delegationMembers){
755 		List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
756 		boolean valid;
757 		int memberCounter = 0;
758 		List<RemotableAttributeError> errorsTemp;
759 		Map<String, String> mapToValidate;
760         KimTypeService kimTypeService;
761         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
762         RoleMemberBo roleMember;
763         String errorPath;
764         ArrayList<String> roleIds;
765         KimType kimType;
766 		for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
767 			kimType = KimTypeBo.to(delegationMember.getRoleBo().getKimRoleType());
768 			kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
769 			roleIds = new ArrayList<String>();
770 			roleIds.add(delegationMember.getRoleBo().getId());
771 			errorPath = "delegationMembers["+memberCounter+"]";
772 			mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers());
773 			errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
774 			validationErrors.addAll(
775 					attributeValidationHelper.convertErrors(errorPath,
776                             attributeValidationHelper.convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()),
777                             errorsTemp));
778 
779 			roleMember = getUIDocumentService().getRoleMember(delegationMember.getRoleMemberId());
780 			if(roleMember==null){
781 				valid = false;
782 				GlobalVariables.getMessageMap().putError("document."+errorPath, RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{});
783 			} else{
784 				kimTypeService.validateUnmodifiableAttributes(kimType.getId(), roleMember.getAttributes(), mapToValidate);
785 				validationErrors.addAll(
786 						attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
787                                 .convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()), errorsTemp));
788 			}
789 	        memberCounter++;
790     	}
791 		GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
792     	if (validationErrors.isEmpty()) {
793     		valid = true;
794     	} else {
795     		attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
796     		valid = false;
797     	}
798     	return valid;
799     }
800 
801 }