View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.document.rule;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreConstants;
20  import org.kuali.rice.core.api.membership.MemberType;
21  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
22  import org.kuali.rice.core.api.uif.RemotableAttributeError;
23  import org.kuali.rice.core.api.util.RiceKeyConstants;
24  import org.kuali.rice.core.api.util.VersionHelper;
25  import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
26  import org.kuali.rice.kim.api.KimConstants;
27  import org.kuali.rice.kim.api.identity.IdentityService;
28  import org.kuali.rice.kim.api.identity.principal.Principal;
29  import org.kuali.rice.kim.api.permission.Permission;
30  import org.kuali.rice.kim.api.responsibility.Responsibility;
31  import org.kuali.rice.kim.api.responsibility.ResponsibilityService;
32  import org.kuali.rice.kim.api.role.Role;
33  import org.kuali.rice.kim.api.role.RoleService;
34  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
35  import org.kuali.rice.kim.api.type.KimAttributeField;
36  import org.kuali.rice.kim.api.type.KimType;
37  import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
38  import org.kuali.rice.kim.bo.ui.KimDocumentRolePermission;
39  import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
40  import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibility;
41  import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibilityAction;
42  import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
43  import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMemberQualifier;
44  import org.kuali.rice.kim.document.IdentityManagementRoleDocument;
45  import org.kuali.rice.kim.framework.role.RoleTypeService;
46  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
47  import org.kuali.rice.kim.framework.type.KimTypeService;
48  import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
49  import org.kuali.rice.kim.impl.responsibility.AddResponsibilityEvent;
50  import org.kuali.rice.kim.impl.responsibility.AddResponsibilityRule;
51  import org.kuali.rice.kim.impl.responsibility.KimDocumentResponsibilityRule;
52  import org.kuali.rice.kim.impl.responsibility.ResponsibilityBo;
53  import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
54  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
55  import org.kuali.rice.kim.impl.type.KimTypeLookupableHelperServiceImpl;
56  import org.kuali.rice.kim.rule.event.ui.AddDelegationEvent;
57  import org.kuali.rice.kim.rule.event.ui.AddDelegationMemberEvent;
58  import org.kuali.rice.kim.rule.event.ui.AddMemberEvent;
59  import org.kuali.rice.kim.rule.event.ui.AddPermissionEvent;
60  import org.kuali.rice.kim.rule.ui.AddDelegationMemberRule;
61  import org.kuali.rice.kim.rule.ui.AddDelegationRule;
62  import org.kuali.rice.kim.rule.ui.AddMemberRule;
63  import org.kuali.rice.kim.rule.ui.AddPermissionRule;
64  import org.kuali.rice.kim.rules.ui.KimDocumentMemberRule;
65  import org.kuali.rice.kim.rules.ui.KimDocumentPermissionRule;
66  import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationMemberRule;
67  import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationRule;
68  import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
69  import org.kuali.rice.krad.document.Document;
70  import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
71  import org.kuali.rice.krad.service.BusinessObjectService;
72  import org.kuali.rice.krad.service.KRADServiceLocator;
73  import org.kuali.rice.krad.util.GlobalVariables;
74  import org.kuali.rice.krad.util.KRADConstants;
75  import org.kuali.rice.krad.util.MessageMap;
76  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
77  import org.kuali.rice.ksb.api.bus.Endpoint;
78  import org.kuali.rice.ksb.api.bus.ServiceBus;
79  
80  import javax.xml.namespace.QName;
81  import java.sql.Timestamp;
82  import java.util.ArrayList;
83  import java.util.Collections;
84  import java.util.HashMap;
85  import java.util.HashSet;
86  import java.util.List;
87  import java.util.Map;
88  import java.util.Set;
89  
90  /**
91   * @author Kuali Rice Team (rice.collab@kuali.org)
92   */
93  public class IdentityManagementRoleDocumentRule extends TransactionalDocumentRuleBase implements AddPermissionRule, AddResponsibilityRule, AddMemberRule, AddDelegationRule, AddDelegationMemberRule {
94  //	protected static final Logger LOG = Logger.getLogger( IdentityManagementRoleDocumentRule.class );
95  
96      public static final int PRIORITY_NUMBER_MIN_VALUE = 1;
97      public static final int PRIORITY_NUMBER_MAX_VALUE = 11;
98  
99  	protected AddResponsibilityRule addResponsibilityRule;
100 	protected AddPermissionRule  addPermissionRule;
101 	protected AddMemberRule  addMemberRule;
102 	protected AddDelegationRule addDelegationRule;
103 	protected AddDelegationMemberRule addDelegationMemberRule;
104 	protected BusinessObjectService businessObjectService;
105 	protected ResponsibilityService responsibilityService;
106 	protected Class<? extends AddResponsibilityRule> addResponsibilityRuleClass = KimDocumentResponsibilityRule.class;
107 	protected Class<? extends AddPermissionRule> addPermissionRuleClass = KimDocumentPermissionRule.class;
108 	protected Class<? extends AddMemberRule> addMemberRuleClass = KimDocumentMemberRule.class;
109 	protected Class<? extends AddDelegationRule> addDelegationRuleClass = RoleDocumentDelegationRule.class;
110 	protected Class<? extends AddDelegationMemberRule> addDelegationMemberRuleClass = RoleDocumentDelegationMemberRule.class;
111 
112 	protected IdentityService identityService;
113 	private static ResponsibilityInternalService responsibilityInternalService;
114 
115 	protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper();
116 
117 	// KULRICE-4153
118 	protected ActiveRoleMemberHelper activeRoleMemberHelper = new ActiveRoleMemberHelper();
119 
120     public IdentityService getIdentityService() {
121         if ( identityService == null) {
122             identityService = KimApiServiceLocator.getIdentityService();
123         }
124         return identityService;
125     }
126 
127     @Override
128     protected boolean processCustomSaveDocumentBusinessRules(Document document) {
129         if (!(document instanceof IdentityManagementRoleDocument)) {
130             return false;
131         }
132 
133         IdentityManagementRoleDocument roleDoc = (IdentityManagementRoleDocument)document;
134 
135         boolean valid = true;
136         boolean validateRoleAssigneesAndDelegations = !KimTypeLookupableHelperServiceImpl
137                 .hasDerivedRoleTypeService(roleDoc.getKimType());
138         GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
139         valid &= validDuplicateRoleName(roleDoc);
140         valid &= validPermissions(roleDoc);
141         valid &= validResponsibilities(roleDoc);
142         //KULRICE-6858 Validate group members are in identity system
143         valid &= validRoleMemberPrincipalIDs(roleDoc.getModifiedMembers());
144         getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false);
145         validateRoleAssigneesAndDelegations &= validAssignRole(roleDoc);
146         if(validateRoleAssigneesAndDelegations){
147 	        //valid &= validAssignRole(roleDoc);
148         	// KULRICE-4153 & KULRICE-4818
149         	// Use the Active Role Member Helper to retrieve only those Role Members & Delegation member that are active
150         	// If a member or delegation is active on a Role, and they have an inactive Role Qualifier, Role Qualifier validation will fail
151         	// If a member or delegation is inactive on a Role, and they have an inactive Role Qualifier, Role Qualifier validation will pass
152             List<KimDocumentRoleMember> activeRoleMembers = activeRoleMemberHelper.getActiveRoleMembers(roleDoc.getMembers());
153             List<KimDocumentRoleMember> newActiveRoleMembers = activeRoleMemberHelper.getActiveRoleMembers(roleDoc.getModifiedMembers());
154             List<RoleDocumentDelegationMember> activeRoleDelegationMembers = activeRoleMemberHelper.getActiveDelegationRoleMembers(roleDoc.getDelegationMembers());
155 
156             valid &= validateRoleQualifier(newActiveRoleMembers, roleDoc.getKimType());
157 	        valid &= validRoleMemberActiveDates(roleDoc.getModifiedMembers());
158 	        valid &= validateDelegationMemberRoleQualifier(newActiveRoleMembers, activeRoleDelegationMembers, roleDoc.getKimType(), activeRoleMembers);
159 	        valid &= validDelegationMemberActiveDates(roleDoc.getDelegationMembers());
160 	        valid &= validRoleMembersResponsibilityActions(roleDoc.getModifiedMembers());
161         }
162         valid &= validRoleResponsibilitiesActions(roleDoc.getResponsibilities());
163         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
164 
165         return valid;
166     }
167 
168 	protected boolean validAssignRole(IdentityManagementRoleDocument document){
169         boolean rulePassed = true;
170         Map<String,String> additionalPermissionDetails = new HashMap<String,String>();
171         additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, document.getRoleNamespace());
172         additionalPermissionDetails.put(KimConstants.AttributeConstants.ROLE_NAME, document.getRoleName());
173 		if((document.getMembers()!=null && document.getMembers().size()>0) ||
174 				(document.getDelegationMembers()!=null && document.getDelegationMembers().size()>0)){
175 			if(!getDocumentDictionaryService().getDocumentAuthorizer(document).isAuthorizedByTemplate(
176 					document, KimConstants.NAMESPACE_CODE, KimConstants.PermissionTemplateNames.ASSIGN_ROLE,
177 					GlobalVariables.getUserSession().getPrincipalId(), additionalPermissionDetails, null)){
178 	            rulePassed = false;
179 			}
180 		}
181 		return rulePassed;
182 	}
183 
184     protected boolean validRoleMemberPrincipalIDs(List<KimDocumentRoleMember> roleMembers) {
185         boolean valid = true;
186         List<String> principalIds = new ArrayList<String>();
187         for(KimDocumentRoleMember roleMember: roleMembers) {
188             if (StringUtils.equals(roleMember.getMemberTypeCode(), KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()) ) {
189                 principalIds.add(roleMember.getMemberId());
190             }
191         }
192         if(!principalIds.isEmpty())       {
193             List<Principal> validPrincipals = getIdentityService().getPrincipals(principalIds);
194             for(KimDocumentRoleMember roleMember: roleMembers) {
195                 if (StringUtils.equals(roleMember.getMemberTypeCode(), MemberType.PRINCIPAL.getCode()) ) {
196                     boolean validPrincipalId = false;
197                     if(validPrincipals != null && !validPrincipals.isEmpty())       {
198                         for(Principal validPrincipal: validPrincipals) {
199                             if(roleMember.getMemberId().equals(validPrincipal.getPrincipalId()))     {
200                              validPrincipalId = true;
201                             }
202                         }
203                     }
204                     if(!validPrincipalId) {
205                         GlobalVariables.getMessageMap().putError("document.member.memberId", RiceKeyConstants.ERROR_MEMBERID_MEMBERTYPE_MISMATCH,
206                                 new String[] {roleMember.getMemberId()});
207                         valid = false;
208                     }
209                 }
210             }
211         }
212         return valid;
213     }
214 
215     @SuppressWarnings("unchecked")
216 	protected boolean validDuplicateRoleName(IdentityManagementRoleDocument roleDoc){
217         Role role = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName(roleDoc.getRoleNamespace(),
218                 roleDoc.getRoleName());
219     	boolean rulePassed = true;
220     	if(role!=null){
221     		if(role.getId().equals(roleDoc.getRoleId())) {
222                 rulePassed = true;
223             }
224     		else{
225 	    		GlobalVariables.getMessageMap().putError("document.roleName",
226 	    				RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Role Name"});
227 	    		rulePassed = false;
228     		}
229     	}
230     	return rulePassed;
231     }
232 
233     protected boolean validRoleMemberActiveDates(List<KimDocumentRoleMember> roleMembers) {
234     	boolean valid = true;
235 		int i = 0;
236     	for(KimDocumentRoleMember roleMember: roleMembers) {
237    			valid &= validateActiveDate("document.members["+i+"].activeToDate", roleMember.getActiveFromDate(), roleMember.getActiveToDate());
238     		i++;
239     	}
240     	return valid;
241     }
242 
243     protected boolean validDelegationMemberActiveDates(List<RoleDocumentDelegationMember> delegationMembers) {
244     	boolean valid = true;
245 		int i = 0;
246     	for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
247    			valid &= validateActiveDate("document.delegationMembers[" + i + "].activeToDate",
248                        delegationMember.getActiveFromDate(), delegationMember.getActiveToDate());
249     		i++;
250     	}
251     	return valid;
252     }
253 
254     protected boolean validPermissions(IdentityManagementRoleDocument document){
255     	Permission kimPermissionInfo;
256     	boolean valid = true;
257     	int i = 0;
258     	for(KimDocumentRolePermission permission: document.getPermissions()){
259     		kimPermissionInfo = permission.getPermission();
260     		if(!permission.isActive() && !hasPermissionToGrantPermission(permission.getPermission(), document)){
261     	        GlobalVariables.getMessageMap().putError("permissions["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_PERMISSION,
262     	        		new String[] {kimPermissionInfo.getNamespaceCode(), kimPermissionInfo.getTemplate().getName()});
263     	        valid = false;
264     		}
265     		i++;
266     	}
267     	return valid;
268     }
269 
270     protected boolean validResponsibilities(IdentityManagementRoleDocument document){
271     	ResponsibilityBo kimResponsibilityImpl;
272     	boolean valid = true;
273     	int i = 0;
274     	for(KimDocumentRoleResponsibility responsibility: document.getResponsibilities()){
275     		kimResponsibilityImpl = responsibility.getKimResponsibility();
276     		if(!responsibility.isActive() && !hasPermissionToGrantResponsibility(ResponsibilityBo.to(responsibility.getKimResponsibility()), document)){
277     	        GlobalVariables.getMessageMap().putError("responsibilities["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_RESPONSIBILITY,
278     	        		new String[] {kimResponsibilityImpl.getNamespaceCode(), kimResponsibilityImpl.getTemplate().getName()});
279     	        valid = false;
280     		}
281     		i++;
282     	}
283     	return valid;
284     }
285 
286     protected boolean validRoleResponsibilitiesActions(List<KimDocumentRoleResponsibility> roleResponsibilities){
287         int i = 0;
288         boolean rulePassed = true;
289     	for(KimDocumentRoleResponsibility roleResponsibility: roleResponsibilities){
290     		if(!getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResponsibility.getResponsibilityId())) {
291     			validateRoleResponsibilityAction("document.responsibilities["+i+"].roleRspActions[0].priorityNumber", roleResponsibility.getRoleRspActions().get(0));
292             }
293         	i++;
294     	}
295     	return rulePassed;
296     }
297 
298     protected boolean validRoleMembersResponsibilityActions(List<KimDocumentRoleMember> roleMembers){
299         int i = 0;
300         int j;
301         boolean rulePassed = true;
302     	for(KimDocumentRoleMember roleMember: roleMembers){
303     		j = 0;
304     		if(roleMember.getRoleRspActions()!=null && !roleMember.getRoleRspActions().isEmpty()){
305 	    		for(KimDocumentRoleResponsibilityAction roleRspAction: roleMember.getRoleRspActions()){
306 	    			validateRoleResponsibilityAction("document.members["+i+"].roleRspActions["+j+"].priorityNumber", roleRspAction);
307 		        	j++;
308 	    		}
309     		}
310     		i++;
311     	}
312     	return rulePassed;
313     }
314 
315     protected boolean validateRoleResponsibilityAction(String errorPath, KimDocumentRoleResponsibilityAction roleRspAction){
316     	boolean rulePassed = true;
317     	/*if(StringUtils.isBlank(roleRspAction.getActionPolicyCode())){
318     		GlobalVariables.getErrorMap().putError(errorPath,
319     				RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Policy Code"});
320     		rulePassed = false;
321     	}
322     	if(roleRspAction.getPriorityNumber()==null){
323     		GlobalVariables.getErrorMap().putError(errorPath,
324     				RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Priority Number"});
325     		rulePassed = false;
326     	}
327     	if(StringUtils.isBlank(roleRspAction.getActionTypeCode())){
328     		GlobalVariables.getErrorMap().putError(errorPath,
329     				RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Type Code"});
330     		rulePassed = false;
331     	}*/
332     	if(roleRspAction.getPriorityNumber()!=null &&
333     			(roleRspAction.getPriorityNumber()<PRIORITY_NUMBER_MIN_VALUE
334     					|| roleRspAction.getPriorityNumber()>PRIORITY_NUMBER_MAX_VALUE)){
335     		GlobalVariables.getMessageMap().putError(errorPath,
336    				RiceKeyConstants.ERROR_PRIORITY_NUMBER_RANGE, new String[] {PRIORITY_NUMBER_MIN_VALUE+"", PRIORITY_NUMBER_MAX_VALUE+""});
337     		rulePassed = false;
338     	}
339 
340     	return rulePassed;
341     }
342 
343     protected boolean validateRoleQualifier(List<KimDocumentRoleMember> roleMembers, KimType kimType){
344 		List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
345 
346 		int memberCounter = 0;
347 		int roleMemberCount = 0;
348 		List<RemotableAttributeError> errorsTemp;
349 		Map<String, String> mapToValidate;
350         KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
351         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
352         final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId());
353         final Set<String> uniqueAttributeNames = figureOutUniqueQualificationSet(roleMembers, attributeDefinitions);
354 
355 		for(KimDocumentRoleMember roleMember: roleMembers) {
356 			errorsTemp = Collections.emptyList();
357 			mapToValidate = attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers());
358 
359             VersionedService<RoleTypeService> versionedRoleTypeService = getVersionedRoleTypeService(kimType);
360             boolean shouldNotValidate = true;
361             if (versionedRoleTypeService != null) {
362                 boolean versionOk = VersionHelper.compareVersion(versionedRoleTypeService.getVersion(),
363                         CoreConstants.Versions.VERSION_2_1_2)!=-1? true:false;
364                 if(versionOk) {
365                     shouldNotValidate = versionedRoleTypeService.getService().shouldValidateQualifiersForMemberType( MemberType.fromCode(roleMember.getMemberTypeCode()));
366                 } else {
367                     shouldNotValidate = false;
368                 }
369             }
370             if(!shouldNotValidate){
371 				errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
372 				validationErrors.addAll(attributeValidationHelper.convertErrorsForMappedFields(
373                         "members[" + memberCounter + "]", errorsTemp));
374 		        memberCounter++;
375 			}
376 			if (uniqueAttributeNames.size() > 0) {
377 				validateUniquePersonRoleQualifiersUniqueForRoleMembership(roleMember, roleMemberCount, roleMembers, uniqueAttributeNames, validationErrors);
378 			}
379 
380 			roleMemberCount += 1;
381     	}
382 
383 		GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
384 
385     	if (validationErrors.isEmpty()) {
386     		return true;
387     	} 
388     	attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
389     	return false;
390     }
391 
392     /**
393      * Finds the names of the unique qualification attributes which this role should be checking against
394      *
395      * @param memberships the memberships (we take the qualification from the first)
396      * @param attributeDefinitions information about the attributeDefinitions
397      * @return a Set of unique attribute ids (with their indices, for error reporting)
398      */
399     protected Set<String> figureOutUniqueQualificationSet(List<KimDocumentRoleMember> memberships, List<KimAttributeField> attributeDefinitions) {
400     	Set<String> uniqueAttributeIds = new HashSet<String>();
401 
402     	if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly
403     		KimDocumentRoleMember membership = memberships.get(0);
404 
405     		for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
406         		if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
407     	    		final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
408                             qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
409 
410     	    		if (relatedDefinition != null && relatedDefinition.isUnique()) {
411     	    			uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set
412     	    		}
413         		}
414     		}
415     	}
416 
417     	return uniqueAttributeIds;
418     }
419 
420     /**
421      * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
422      *
423      * @param membershipToCheck the membership to check
424      * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
425      * @param validationErrors Map<String, String> of errors to report
426      * @return true if all unique values are indeed unique, false otherwise
427      */
428     protected boolean validateUniquePersonRoleQualifiersUniqueForRoleMembership(KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, List<KimDocumentRoleMember> memberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) {
429     	boolean foundError = false;
430     	int count = 0;
431 
432     	for (KimDocumentRoleMember membership : memberships) {
433     		if (membershipToCheckIndex != count) {
434     			if (sameMembership(membershipToCheck, membership)) {
435     				if (sameUniqueMembershipQualifications(membershipToCheck, membership, uniqueQualifierIds)) {
436     					foundError = true;
437     					// add error to each qualifier which is supposed to be unique
438     					int qualifierCount = 0;
439 
440     					for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
441     						if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) {
442                                 // for new member lines, KimAttribute is not preloaded
443                                 // make sure to load it here in order to obtain the name for use in error message
444                                 KimAttributeBo attr = qualifier.getKimAttribute();
445                                 String attrName = "<unknown>";
446                                 if (attr == null && qualifier.getKimAttrDefnId() != null) {
447                                     attr = KRADServiceLocator.getBusinessObjectService().findBySinglePrimaryKey(KimAttributeBo.class, qualifier.getKimAttrDefnId());
448                                 }
449                                 if (attr != null) {
450                                     attrName = attr.getAttributeName();
451                                 }
452                                 validationErrors.add(RemotableAttributeError.Builder.create("document.members["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+membership.getMemberId()+";"+attrName+";"+qualifier.getAttrVal()).build());
453     						}
454     						qualifierCount += 1;
455     					}
456     				}
457     			}
458     		}
459     		count += 1;
460     	}
461 
462     	return foundError;
463     }
464 
465     /**
466      * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id
467      *
468      * @param membershipA the first membership to check
469      * @param membershipB the second membership to check
470      * @return true if the two memberships represent the same member; false if they do not, or if it could not be profitably determined if the members were the same
471      */
472     protected boolean sameMembership(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB) {
473     	if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) {
474     		return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId());
475     	}
476     	return false;
477     }
478 
479     /**
480      * Given two memberships which represent the same member, do they share qualifications?
481      *
482      * @param membershipA the first membership to check
483      * @param membershipB the second membership to check
484      * @param uniqueAttributeIds the Set of attribute definition ids which should be unique
485      * @return
486      */
487     protected boolean sameUniqueMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueAttributeIds) {
488     	boolean equalSoFar = true;
489     	for (String kimAttributeDefinitionId : uniqueAttributeIds) {
490     		final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId);
491     		final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId);
492 
493     		if (qualifierA != null && qualifierB != null) {
494     			equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
495     		}
496     	}
497     	return equalSoFar;
498     }
499 
500     protected KimDocumentRoleMember getRoleMemberForDelegation(
501     		List<KimDocumentRoleMember> roleMembers, RoleDocumentDelegationMember delegationMember, List<KimDocumentRoleMember> modifiedRoleMembers) {
502     	if((roleMembers==null && modifiedRoleMembers==null) || delegationMember==null || delegationMember.getRoleMemberId()==null) { return null; }
503         for(KimDocumentRoleMember roleMember: modifiedRoleMembers){
504             if(delegationMember.getRoleMemberId().equals(roleMember.getRoleMemberId())) {
505                 return roleMember;
506             }
507         }
508     	for(KimDocumentRoleMember roleMember: roleMembers){
509     		if(delegationMember.getRoleMemberId().equals(roleMember.getRoleMemberId())) {
510                 return roleMember;
511             }
512     	}
513     	return null;
514     }
515 
516     protected boolean validateDelegationMemberRoleQualifier(List<KimDocumentRoleMember> modifiedRoleMembers,
517     		List<RoleDocumentDelegationMember> delegationMembers, KimType kimType, List<KimDocumentRoleMember> nonModifiedRoleMembers){
518 		List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
519 		boolean valid;
520 		int memberCounter = 0;
521 		List<RemotableAttributeError> errorsTemp;
522 		Map<String, String> mapToValidate;
523         KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
524         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
525         KimDocumentRoleMember roleMember;
526         String errorPath;
527         final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId());
528         final Set<String> uniqueQualifierAttributes = figureOutUniqueQualificationSetForDelegation(delegationMembers, attributeDefinitions);
529 
530 		for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
531 			errorPath = "delegationMembers["+memberCounter+"]";
532 			mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers());
533 			if(!delegationMember.isRole()){
534 				errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
535 				validationErrors.addAll(
536 						attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp));
537 			}
538 			roleMember = getRoleMemberForDelegation(nonModifiedRoleMembers, delegationMember, modifiedRoleMembers);
539 			if(roleMember==null){
540 				valid = false;
541 				GlobalVariables.getMessageMap().putError("document.delegationMembers["+memberCounter+"]", RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{});
542 			} else{
543 				errorsTemp = kimTypeService.validateUnmodifiableAttributes(
544 								kimType.getId(),
545 								attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers()),
546 								mapToValidate);
547 				validationErrors.addAll(
548 						attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp) );
549 			}
550 			if (uniqueQualifierAttributes.size() > 0) {
551 				validateUniquePersonRoleQualifiersUniqueForRoleDelegation(delegationMember, memberCounter, delegationMembers, uniqueQualifierAttributes, validationErrors);
552 			}
553 	        memberCounter++;
554     	}
555 		GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
556     	if (validationErrors.isEmpty()) {
557     		valid = true;
558     	} else {
559     		attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
560     		valid = false;
561     	}
562     	return valid;
563     }
564 
565     /**
566      * Finds the names of the unique qualification attributes which this role should be checking against
567      *
568      * @param memberships the memberships (we take the qualification from the first)
569      * @param attributeDefinitions information about the attributeDefinitions
570      * @return a Set of unique attribute ids (with their indices, for error reporting)
571      */
572     protected Set<String> figureOutUniqueQualificationSetForDelegation(List<RoleDocumentDelegationMember> memberships, List<KimAttributeField> attributeDefinitions) {
573     	Set<String> uniqueAttributeIds = new HashSet<String>();
574 
575     	if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly
576     		RoleDocumentDelegationMember membership = memberships.get(0);
577 
578     		for (RoleDocumentDelegationMemberQualifier qualifier : membership.getQualifiers()) {
579         		if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
580     	    		final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
581                             qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
582 
583     	    		if (relatedDefinition.isUnique()) {
584     	    			uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set
585     	    		}
586         		}
587     		}
588     	}
589 
590     	return uniqueAttributeIds;
591     }
592 
593     /**
594      * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
595      *
596      * @param delegationMembershipToCheck the membership to check
597      * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
598      * @param validationErrors Map<String, String> of errors to report
599      * @return true if all unique values are indeed unique, false otherwise
600      */
601     protected boolean validateUniquePersonRoleQualifiersUniqueForRoleDelegation(RoleDocumentDelegationMember delegationMembershipToCheck, int membershipToCheckIndex, List<RoleDocumentDelegationMember> delegationMemberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) {
602     	boolean foundError = false;
603     	int count = 0;
604 
605     	for (RoleDocumentDelegationMember delegationMembership : delegationMemberships) {
606     		if (membershipToCheckIndex != count) {
607     			if (sameDelegationMembership(delegationMembershipToCheck, delegationMembership)) {
608     				if (sameUniqueDelegationMembershipQualifications(delegationMembershipToCheck, delegationMembership, uniqueQualifierIds)) {
609     					foundError = true;
610     					// add error to each qualifier which is supposed to be unique
611     					int qualifierCount = 0;
612 
613     					for (RoleDocumentDelegationMemberQualifier qualifier : delegationMembership.getQualifiers()) {
614     						if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) {
615     							validationErrors.add(RemotableAttributeError.Builder.create("document.delegationMembers["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+qualifier.getKimAttribute().getAttributeName()+";"+qualifier.getAttrVal()).build());
616     						}
617     						qualifierCount += 1;
618     					}
619     				}
620     			}
621     		}
622     		count += 1;
623     	}
624 
625     	return foundError;
626     }
627 
628     /**
629      * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id
630      *
631      * @param membershipA the first membership to check
632      * @param membershipB the second membership to check
633      * @return true if the two memberships represent the same member; false if they do not, or if it could not be profitably determined if the members were the same
634      */
635     protected boolean sameDelegationMembership(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB) {
636     	if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) {
637     		return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId());
638     	}
639     	return false;
640     }
641 
642     /**
643      * Given two memberships which represent the same member, do they share qualifications?
644      *
645      * @param membershipA the first membership to check
646      * @param membershipB the second membership to check
647      * @param uniqueAttributeIds the Set of attribute definition ids which should be unique
648      * @return
649      */
650     protected boolean sameUniqueDelegationMembershipQualifications(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB, Set<String> uniqueAttributeIds) {
651     	boolean equalSoFar = true;
652     	for (String kimAttributeDefinitionId : uniqueAttributeIds) {
653     		final RoleDocumentDelegationMemberQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId);
654     		final RoleDocumentDelegationMemberQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId);
655 
656     		if (qualifierA != null && qualifierB != null) {
657     			equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
658     		}
659     	}
660     	return equalSoFar;
661     }
662 
663 	protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
664 		// TODO : do not have detail bus rule yet, so just check this for now.
665 		boolean valid = true;
666 		if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
667 	        MessageMap errorMap = GlobalVariables.getMessageMap();
668             errorMap.putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
669             valid = false;
670 
671 		}
672 		return valid;
673 	}
674 
675 	/**
676 	 *
677 	 * This method checks to see if adding a role to role membership
678 	 * creates a circular reference.
679 	 *
680 	 * @param addMemberEvent
681 	 * @return true  - ok to assign, no circular references
682 	 *         false - do not make assignment, will create circular reference.
683 	 */
684 	protected boolean checkForCircularRoleMembership(AddMemberEvent addMemberEvent)
685 	{
686 		KimDocumentRoleMember newMember = addMemberEvent.getMember();
687 		if (newMember == null || StringUtils.isBlank(newMember.getMemberId())){
688 			MessageMap errorMap = GlobalVariables.getMessageMap();
689 			errorMap.putError("member.memberId", RiceKeyConstants.ERROR_INVALID_ROLE, new String[] {""});
690 			return false;
691 		}
692 		Set<String> roleMemberIds = null;
693 		// if the role member is a role, check to make sure we won't be creating a circular reference.
694 		// Verify that the new role is not already related to the role either directly or indirectly
695 		if (newMember.isRole()){
696 			// get all nested role member ids that are of type role
697 			RoleService roleService = KimApiServiceLocator.getRoleService();
698 			roleMemberIds = roleService.getRoleTypeRoleMemberIds(newMember.getMemberId());
699 
700 			// check to see if the document role is not a member of the new member role
701 			IdentityManagementRoleDocument document = (IdentityManagementRoleDocument)addMemberEvent.getDocument();
702 			if (roleMemberIds.contains(document.getRoleId())){
703 				MessageMap errorMap = GlobalVariables.getMessageMap();
704 				errorMap.putError("member.memberId", RiceKeyConstants.ERROR_ASSIGN_ROLE_MEMBER_CIRCULAR, new String[] {newMember.getMemberId()});
705 				return false;
706 			}
707 		}
708 		return true;
709 	}
710 
711 	/**
712 	 * @return the addResponsibilityRule
713 	 */
714 	public AddResponsibilityRule getAddResponsibilityRule() {
715 		if(addResponsibilityRule == null){
716 			try {
717 				addResponsibilityRule = addResponsibilityRuleClass.newInstance();
718 			} catch ( Exception ex ) {
719 				throw new RuntimeException( "Unable to create AddResponsibilityRule instance using class: " + addResponsibilityRuleClass, ex );
720 			}
721 		}
722 		return addResponsibilityRule;
723 	}
724 
725 	/**
726 	 * @return the addPermissionRule
727 	 */
728 	public AddPermissionRule getAddPermissionRule() {
729 		if(addPermissionRule == null){
730 			try {
731 				addPermissionRule = addPermissionRuleClass.newInstance();
732 			} catch ( Exception ex ) {
733 				throw new RuntimeException( "Unable to create AddPermissionRule instance using class: " + addPermissionRuleClass, ex );
734 			}
735 		}
736 		return addPermissionRule;
737 	}
738 
739 	/**
740 	 * @return the addMemberRule
741 	 */
742 	public AddMemberRule getAddMemberRule() {
743 		if(addMemberRule == null){
744 			try {
745 				addMemberRule = addMemberRuleClass.newInstance();
746 			} catch ( Exception ex ) {
747 				throw new RuntimeException( "Unable to create AddMemberRule instance using class: " + addMemberRuleClass, ex );
748 			}
749 		}
750 		return addMemberRule;
751 	}
752 
753 	/**
754 	 * @return the addDelegationRule
755 	 */
756 	public AddDelegationRule getAddDelegationRule() {
757 		if(addDelegationRule == null){
758 			try {
759 				addDelegationRule = addDelegationRuleClass.newInstance();
760 			} catch ( Exception ex ) {
761 				throw new RuntimeException( "Unable to create AddDelegationRule instance using class: " + addDelegationRuleClass, ex );
762 			}
763 		}
764 		return addDelegationRule;
765 	}
766 
767 	/**
768 	 * @return the addDelegationMemberRule
769 	 */
770 	public AddDelegationMemberRule getAddDelegationMemberRule() {
771 		if(addDelegationMemberRule == null){
772 			try {
773 				addDelegationMemberRule = addDelegationMemberRuleClass.newInstance();
774 			} catch ( Exception ex ) {
775 				throw new RuntimeException( "Unable to create AddDelegationMemberRule instance using class: " + addDelegationMemberRuleClass, ex );
776 			}
777 		}
778 		return addDelegationMemberRule;
779 	}
780 
781     public boolean processAddPermission(AddPermissionEvent addPermissionEvent) {
782         return getAddPermissionRule().processAddPermission(addPermissionEvent);
783     }
784 
785     public boolean hasPermissionToGrantPermission(Permission kimPermissionInfo , IdentityManagementRoleDocument document){
786         return getAddPermissionRule().hasPermissionToGrantPermission(kimPermissionInfo, document);
787     }
788 
789     public boolean processAddResponsibility(AddResponsibilityEvent addResponsibilityEvent) {
790         return getAddResponsibilityRule().processAddResponsibility(addResponsibilityEvent);
791     }
792 
793     public boolean hasPermissionToGrantResponsibility(Responsibility kimResponsibilityInfo, IdentityManagementRoleDocument document) {
794         return getAddResponsibilityRule().hasPermissionToGrantResponsibility(kimResponsibilityInfo, document);
795     }
796 
797     public boolean processAddMember(AddMemberEvent addMemberEvent) {
798         boolean success = new KimDocumentMemberRule().processAddMember(addMemberEvent);
799         success &= validateActiveDate("member.activeFromDate", addMemberEvent.getMember().getActiveFromDate(), addMemberEvent.getMember().getActiveToDate());
800         success &= checkForCircularRoleMembership(addMemberEvent);
801         return success;
802     }
803 
804     public boolean processAddDelegation(AddDelegationEvent addDelegationEvent) {
805         return getAddDelegationRule().processAddDelegation(addDelegationEvent);
806     }
807 
808     public boolean processAddDelegationMember(AddDelegationMemberEvent addDelegationMemberEvent) {
809         boolean success = new RoleDocumentDelegationMemberRule().processAddDelegationMember(addDelegationMemberEvent);
810         RoleDocumentDelegationMember roleDocumentDelegationMember = addDelegationMemberEvent.getDelegationMember();
811         success &= validateActiveDate("delegationMember.activeFromDate", roleDocumentDelegationMember.getActiveFromDate(), roleDocumentDelegationMember.getActiveToDate());
812         return success;
813     }
814 
815 	public ResponsibilityService getResponsibilityService() {
816 		if(responsibilityService == null){
817 			responsibilityService = KimApiServiceLocator.getResponsibilityService();
818 		}
819 		return responsibilityService;
820 	}
821 
822     public ResponsibilityInternalService getResponsibilityInternalService() {
823     	if ( responsibilityInternalService == null ) {
824     		responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
825     	}
826 		return responsibilityInternalService;
827 	}
828 
829 
830 	/**
831 	 * @return the businessObjectService
832 	 */
833 	public BusinessObjectService getBusinessObjectService() {
834 		if(businessObjectService == null){
835 			businessObjectService = KRADServiceLocator.getBusinessObjectService();
836 		}
837 		return businessObjectService;
838 	}
839 
840 
841     protected RoleTypeService getRoleTypeService(KimType typeInfo) {
842         String serviceName = typeInfo.getServiceName();
843         if (serviceName != null) {
844             try {
845                 KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
846                 if (service != null && service instanceof RoleTypeService) {
847                     return (RoleTypeService) service;
848                 }
849                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
850             } catch (Exception ex) {
851                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
852             }
853         }
854         return null;
855     }
856 
857     private static class VersionedService<T> {
858 
859         String version;
860         T service;
861 
862         VersionedService(String version, T service) {
863             this.version = version;
864             this.service = service;
865         }
866 
867         T getService() {
868             return this.service;
869         }
870 
871         String getVersion() {
872             return this.version;
873         }
874 
875     }
876 
877     protected VersionedService<RoleTypeService> getVersionedRoleTypeService(KimType typeInfo) {
878         String serviceName = typeInfo.getServiceName();
879         if (serviceName != null) {
880             String version = "2.0.0"; // default version since the base services have been available since then
881             RoleTypeService roleTypeService = null;
882             try {
883 
884                 ServiceBus serviceBus = KsbApiServiceLocator.getServiceBus();
885                 Endpoint endpoint = serviceBus.getEndpoint(QName.valueOf(serviceName));
886                 if (endpoint != null) {
887                     version = endpoint.getServiceConfiguration().getServiceVersion();
888                 }
889                 KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
890                 if (service != null && service instanceof RoleTypeService) {
891                     roleTypeService = (RoleTypeService) service;
892                 } else {
893                     roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
894                 }
895             } catch (Exception ex) {
896                 roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
897             }
898             return new VersionedService<RoleTypeService>(version, roleTypeService);
899         }
900         return null;
901     }
902 
903 }