View Javadoc

1   /**
2    * Copyright 2005-2012 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.krad.document.Document;
60  import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase;
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  
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 	        	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 }