View Javadoc

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