View Javadoc

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