View Javadoc

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