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