View Javadoc

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