001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.kim.document.rule;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.uif.RemotableAttributeError;
020    import org.kuali.rice.core.api.util.RiceKeyConstants;
021    import org.kuali.rice.kim.api.KimConstants;
022    import org.kuali.rice.kim.api.identity.IdentityService;
023    import org.kuali.rice.kim.api.permission.Permission;
024    import org.kuali.rice.kim.api.responsibility.Responsibility;
025    import org.kuali.rice.kim.api.responsibility.ResponsibilityService;
026    import org.kuali.rice.kim.api.role.Role;
027    import org.kuali.rice.kim.api.role.RoleService;
028    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
029    import org.kuali.rice.kim.api.type.KimAttributeField;
030    import org.kuali.rice.kim.api.type.KimType;
031    import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
032    import org.kuali.rice.kim.bo.ui.KimDocumentRolePermission;
033    import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
034    import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibility;
035    import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibilityAction;
036    import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
037    import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMemberQualifier;
038    import org.kuali.rice.kim.document.IdentityManagementRoleDocument;
039    import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
040    import org.kuali.rice.kim.framework.type.KimTypeService;
041    import org.kuali.rice.kim.impl.responsibility.AddResponsibilityEvent;
042    import org.kuali.rice.kim.impl.responsibility.AddResponsibilityRule;
043    import org.kuali.rice.kim.impl.responsibility.KimDocumentResponsibilityRule;
044    import org.kuali.rice.kim.impl.responsibility.ResponsibilityBo;
045    import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
046    import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
047    import org.kuali.rice.kim.impl.type.KimTypeLookupableHelperServiceImpl;
048    import org.kuali.rice.kim.rule.event.ui.AddDelegationEvent;
049    import org.kuali.rice.kim.rule.event.ui.AddDelegationMemberEvent;
050    import org.kuali.rice.kim.rule.event.ui.AddMemberEvent;
051    import org.kuali.rice.kim.rule.event.ui.AddPermissionEvent;
052    import org.kuali.rice.kim.rule.ui.AddDelegationMemberRule;
053    import org.kuali.rice.kim.rule.ui.AddDelegationRule;
054    import org.kuali.rice.kim.rule.ui.AddMemberRule;
055    import org.kuali.rice.kim.rule.ui.AddPermissionRule;
056    import org.kuali.rice.kim.rules.ui.KimDocumentMemberRule;
057    import org.kuali.rice.kim.rules.ui.KimDocumentPermissionRule;
058    import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationMemberRule;
059    import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationRule;
060    import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
061    import org.kuali.rice.krad.document.Document;
062    import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase;
063    import org.kuali.rice.krad.service.BusinessObjectService;
064    import org.kuali.rice.krad.service.KRADServiceLocator;
065    import org.kuali.rice.krad.util.GlobalVariables;
066    import org.kuali.rice.krad.util.KRADConstants;
067    import org.kuali.rice.krad.util.MessageMap;
068    
069    import java.sql.Timestamp;
070    import java.util.ArrayList;
071    import java.util.Collections;
072    import java.util.HashMap;
073    import java.util.HashSet;
074    import java.util.List;
075    import java.util.Map;
076    import java.util.Set;
077    
078    /**
079     * @author Kuali Rice Team (rice.collab@kuali.org)
080     */
081    public class IdentityManagementRoleDocumentRule extends TransactionalDocumentRuleBase implements AddPermissionRule, AddResponsibilityRule, AddMemberRule, AddDelegationRule, AddDelegationMemberRule {
082    //      protected static final Logger LOG = Logger.getLogger( IdentityManagementRoleDocumentRule.class );
083    
084        public static final int PRIORITY_NUMBER_MIN_VALUE = 1;
085        public static final int PRIORITY_NUMBER_MAX_VALUE = 11;
086    
087            protected AddResponsibilityRule addResponsibilityRule;
088            protected AddPermissionRule  addPermissionRule;
089            protected AddMemberRule  addMemberRule;
090            protected AddDelegationRule addDelegationRule;
091            protected AddDelegationMemberRule addDelegationMemberRule;
092            protected BusinessObjectService businessObjectService;
093            protected ResponsibilityService responsibilityService;
094            protected Class<? extends AddResponsibilityRule> addResponsibilityRuleClass = KimDocumentResponsibilityRule.class;
095            protected Class<? extends AddPermissionRule> addPermissionRuleClass = KimDocumentPermissionRule.class;
096            protected Class<? extends AddMemberRule> addMemberRuleClass = KimDocumentMemberRule.class;
097            protected Class<? extends AddDelegationRule> addDelegationRuleClass = RoleDocumentDelegationRule.class;
098            protected Class<? extends AddDelegationMemberRule> addDelegationMemberRuleClass = RoleDocumentDelegationMemberRule.class;
099    
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(!getDocumentDictionaryService().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().getRoleByNamespaceCodeAndName(roleDoc.getRoleNamespace(),
172                    roleDoc.getRoleName());
173            boolean rulePassed = true;
174            if(role!=null){
175                    if(role.getId().equals(roleDoc.getRoleId())) {
176                    rulePassed = true;
177                }
178                    else{
179                            GlobalVariables.getMessageMap().putError("document.roleName",
180                                            RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Role Name"});
181                            rulePassed = false;
182                    }
183            }
184            return rulePassed;
185        }
186    
187        protected boolean validRoleMemberActiveDates(List<KimDocumentRoleMember> roleMembers) {
188            boolean valid = true;
189                    int i = 0;
190            for(KimDocumentRoleMember roleMember: roleMembers) {
191                            valid &= validateActiveDate("document.members["+i+"].activeToDate", roleMember.getActiveFromDate(), roleMember.getActiveToDate());
192                    i++;
193            }
194            return valid;
195        }
196    
197        protected boolean validDelegationMemberActiveDates(List<RoleDocumentDelegationMember> delegationMembers) {
198            boolean valid = true;
199                    int i = 0;
200            for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
201                            valid &= validateActiveDate("document.delegationMembers[" + i + "].activeToDate",
202                           delegationMember.getActiveFromDate(), delegationMember.getActiveToDate());
203                    i++;
204            }
205            return valid;
206        }
207    
208        protected boolean validPermissions(IdentityManagementRoleDocument document){
209            Permission kimPermissionInfo;
210            boolean valid = true;
211            int i = 0;
212            for(KimDocumentRolePermission permission: document.getPermissions()){
213                    kimPermissionInfo = permission.getPermission();
214                    if(!permission.isActive() && !hasPermissionToGrantPermission(permission.getPermission(), document)){
215                    GlobalVariables.getMessageMap().putError("permissions["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_PERMISSION,
216                                    new String[] {kimPermissionInfo.getNamespaceCode(), kimPermissionInfo.getTemplate().getName()});
217                    valid = false;
218                    }
219                    i++;
220            }
221            return valid;
222        }
223    
224        protected boolean validResponsibilities(IdentityManagementRoleDocument document){
225            ResponsibilityBo kimResponsibilityImpl;
226            boolean valid = true;
227            int i = 0;
228            for(KimDocumentRoleResponsibility responsibility: document.getResponsibilities()){
229                    kimResponsibilityImpl = responsibility.getKimResponsibility();
230                    if(!responsibility.isActive() && !hasPermissionToGrantResponsibility(ResponsibilityBo.to(responsibility.getKimResponsibility()), document)){
231                    GlobalVariables.getMessageMap().putError("responsibilities["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_RESPONSIBILITY,
232                                    new String[] {kimResponsibilityImpl.getNamespaceCode(), kimResponsibilityImpl.getTemplate().getName()});
233                    valid = false;
234                    }
235                    i++;
236            }
237            return valid;
238        }
239    
240        protected boolean validRoleResponsibilitiesActions(List<KimDocumentRoleResponsibility> roleResponsibilities){
241            int i = 0;
242            boolean rulePassed = true;
243            for(KimDocumentRoleResponsibility roleResponsibility: roleResponsibilities){
244                    if(!getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResponsibility.getResponsibilityId())) {
245                            validateRoleResponsibilityAction("document.responsibilities["+i+"].roleRspActions[0].priorityNumber", roleResponsibility.getRoleRspActions().get(0));
246                }
247                    i++;
248            }
249            return rulePassed;
250        }
251    
252        protected boolean validRoleMembersResponsibilityActions(List<KimDocumentRoleMember> roleMembers){
253            int i = 0;
254            int j;
255            boolean rulePassed = true;
256            for(KimDocumentRoleMember roleMember: roleMembers){
257                    j = 0;
258                    if(roleMember.getRoleRspActions()!=null && !roleMember.getRoleRspActions().isEmpty()){
259                            for(KimDocumentRoleResponsibilityAction roleRspAction: roleMember.getRoleRspActions()){
260                                    validateRoleResponsibilityAction("document.members["+i+"].roleRspActions["+j+"].priorityNumber", roleRspAction);
261                                    j++;
262                            }
263                    }
264                    i++;
265            }
266            return rulePassed;
267        }
268    
269        protected boolean validateRoleResponsibilityAction(String errorPath, KimDocumentRoleResponsibilityAction roleRspAction){
270            boolean rulePassed = true;
271            /*if(StringUtils.isBlank(roleRspAction.getActionPolicyCode())){
272                    GlobalVariables.getErrorMap().putError(errorPath,
273                                    RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Policy Code"});
274                    rulePassed = false;
275            }
276            if(roleRspAction.getPriorityNumber()==null){
277                    GlobalVariables.getErrorMap().putError(errorPath,
278                                    RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Priority Number"});
279                    rulePassed = false;
280            }
281            if(StringUtils.isBlank(roleRspAction.getActionTypeCode())){
282                    GlobalVariables.getErrorMap().putError(errorPath,
283                                    RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Type Code"});
284                    rulePassed = false;
285            }*/
286            if(roleRspAction.getPriorityNumber()!=null &&
287                            (roleRspAction.getPriorityNumber()<PRIORITY_NUMBER_MIN_VALUE
288                                            || roleRspAction.getPriorityNumber()>PRIORITY_NUMBER_MAX_VALUE)){
289                    GlobalVariables.getMessageMap().putError(errorPath,
290                                    RiceKeyConstants.ERROR_PRIORITY_NUMBER_RANGE, new String[] {PRIORITY_NUMBER_MIN_VALUE+"", PRIORITY_NUMBER_MAX_VALUE+""});
291                    rulePassed = false;
292            }
293    
294            return rulePassed;
295        }
296    
297        protected boolean validateRoleQualifier(List<KimDocumentRoleMember> roleMembers, KimType kimType){
298                    List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
299    
300                    int memberCounter = 0;
301                    int roleMemberCount = 0;
302                    List<RemotableAttributeError> errorsTemp;
303                    Map<String, String> mapToValidate;
304            KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
305            GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
306            final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId());
307            final Set<String> uniqueAttributeNames = figureOutUniqueQualificationSet(roleMembers, attributeDefinitions);
308    
309                    for(KimDocumentRoleMember roleMember: roleMembers) {
310                            errorsTemp = Collections.emptyList();
311                            mapToValidate = attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers());
312                            if(!roleMember.isRole()){
313                                    errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
314                                    validationErrors.addAll(attributeValidationHelper.convertErrorsForMappedFields(
315                            "document.members[" + memberCounter + "]", errorsTemp));
316                            memberCounter++;
317                            }
318                            if (uniqueAttributeNames.size() > 0) {
319                                    validateUniquePersonRoleQualifiersUniqueForRoleMembership(roleMember, roleMemberCount, roleMembers, uniqueAttributeNames, validationErrors);
320                            }
321    
322                            roleMemberCount += 1;
323            }
324    
325                    GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
326    
327            if (validationErrors.isEmpty()) {
328                    return true;
329            } 
330            attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
331            return false;
332        }
333    
334        /**
335         * Finds the names of the unique qualification attributes which this role should be checking against
336         *
337         * @param memberships the memberships (we take the qualification from the first)
338         * @param attributeDefinitions information about the attributeDefinitions
339         * @return a Set of unique attribute ids (with their indices, for error reporting)
340         */
341        protected Set<String> figureOutUniqueQualificationSet(List<KimDocumentRoleMember> memberships, List<KimAttributeField> attributeDefinitions) {
342            Set<String> uniqueAttributeIds = new HashSet<String>();
343    
344            if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly
345                    KimDocumentRoleMember membership = memberships.get(0);
346    
347                    for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
348                            if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
349                            final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
350                                qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
351    
352                            if (relatedDefinition != null && relatedDefinition.isUnique()) {
353                                    uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set
354                            }
355                            }
356                    }
357            }
358    
359            return uniqueAttributeIds;
360        }
361    
362        /**
363         * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
364         *
365         * @param membershipToCheck the membership to check
366         * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
367         * @param validationErrors Map<String, String> of errors to report
368         * @return true if all unique values are indeed unique, false otherwise
369         */
370        protected boolean validateUniquePersonRoleQualifiersUniqueForRoleMembership(KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, List<KimDocumentRoleMember> memberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) {
371            boolean foundError = false;
372            int count = 0;
373    
374            for (KimDocumentRoleMember membership : memberships) {
375                    if (membershipToCheckIndex != count) {
376                            if (sameMembership(membershipToCheck, membership)) {
377                                    if (sameUniqueMembershipQualifications(membershipToCheck, membership, uniqueQualifierIds)) {
378                                            foundError = true;
379                                            // add error to each qualifier which is supposed to be unique
380                                            int qualifierCount = 0;
381    
382                                            for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
383                                                    if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) {
384                                                            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());
385                                                    }
386                                                    qualifierCount += 1;
387                                            }
388                                    }
389                            }
390                    }
391                    count += 1;
392            }
393    
394            return foundError;
395        }
396    
397        /**
398         * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id
399         *
400         * @param membershipA the first membership to check
401         * @param membershipB the second membership to check
402         * @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
403         */
404        protected boolean sameMembership(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB) {
405            if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) {
406                    return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId());
407            }
408            return false;
409        }
410    
411        /**
412         * Given two memberships which represent the same member, do they share qualifications?
413         *
414         * @param membershipA the first membership to check
415         * @param membershipB the second membership to check
416         * @param uniqueAttributeIds the Set of attribute definition ids which should be unique
417         * @return
418         */
419        protected boolean sameUniqueMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueAttributeIds) {
420            boolean equalSoFar = true;
421            for (String kimAttributeDefinitionId : uniqueAttributeIds) {
422                    final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId);
423                    final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId);
424    
425                    if (qualifierA != null && qualifierB != null) {
426                            equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
427                    }
428            }
429            return equalSoFar;
430        }
431    
432        protected KimDocumentRoleMember getRoleMemberForDelegation(
433                    List<KimDocumentRoleMember> roleMembers, RoleDocumentDelegationMember delegationMember){
434            if(roleMembers==null || delegationMember==null || delegationMember.getRoleMemberId()==null) { return null; }
435            for(KimDocumentRoleMember roleMember: roleMembers){
436                    if(delegationMember.getRoleMemberId().equals(roleMember.getRoleMemberId())) {
437                    return roleMember;
438                }
439            }
440            return null;
441        }
442    
443        protected boolean validateDelegationMemberRoleQualifier(List<KimDocumentRoleMember> roleMembers,
444                    List<RoleDocumentDelegationMember> delegationMembers, KimType kimType){
445                    List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
446                    boolean valid;
447                    int memberCounter = 0;
448                    List<RemotableAttributeError> errorsTemp;
449                    Map<String, String> mapToValidate;
450            KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
451            GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
452            KimDocumentRoleMember roleMember;
453            String errorPath;
454            final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId());
455            final Set<String> uniqueQualifierAttributes = figureOutUniqueQualificationSetForDelegation(delegationMembers, attributeDefinitions);
456    
457                    for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
458                            errorPath = "delegationMembers["+memberCounter+"]";
459                            mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers());
460                            if(!delegationMember.isRole()){
461                                    errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
462                                    validationErrors.addAll(
463                                                    attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp));
464                            }
465                            roleMember = getRoleMemberForDelegation(roleMembers, delegationMember);
466                            if(roleMember==null){
467                                    valid = false;
468                                    GlobalVariables.getMessageMap().putError("document.delegationMembers["+memberCounter+"]", RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{});
469                            } else{
470                                    errorsTemp = kimTypeService.validateUnmodifiableAttributes(
471                                                                    kimType.getId(),
472                                                                    attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers()),
473                                                                    mapToValidate);
474                                    validationErrors.addAll(
475                                                    attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp) );
476                            }
477                            if (uniqueQualifierAttributes.size() > 0) {
478                                    validateUniquePersonRoleQualifiersUniqueForRoleDelegation(delegationMember, memberCounter, delegationMembers, uniqueQualifierAttributes, validationErrors);
479                            }
480                    memberCounter++;
481            }
482                    GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
483            if (validationErrors.isEmpty()) {
484                    valid = true;
485            } else {
486                    attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
487                    valid = false;
488            }
489            return valid;
490        }
491    
492        /**
493         * Finds the names of the unique qualification attributes which this role should be checking against
494         *
495         * @param memberships the memberships (we take the qualification from the first)
496         * @param attributeDefinitions information about the attributeDefinitions
497         * @return a Set of unique attribute ids (with their indices, for error reporting)
498         */
499        protected Set<String> figureOutUniqueQualificationSetForDelegation(List<RoleDocumentDelegationMember> memberships, List<KimAttributeField> attributeDefinitions) {
500            Set<String> uniqueAttributeIds = new HashSet<String>();
501    
502            if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly
503                    RoleDocumentDelegationMember membership = memberships.get(0);
504    
505                    for (RoleDocumentDelegationMemberQualifier qualifier : membership.getQualifiers()) {
506                            if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
507                            final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
508                                qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
509    
510                            if (relatedDefinition.isUnique()) {
511                                    uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set
512                            }
513                            }
514                    }
515            }
516    
517            return uniqueAttributeIds;
518        }
519    
520        /**
521         * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
522         *
523         * @param delegationMembershipToCheck the membership to check
524         * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
525         * @param validationErrors Map<String, String> of errors to report
526         * @return true if all unique values are indeed unique, false otherwise
527         */
528        protected boolean validateUniquePersonRoleQualifiersUniqueForRoleDelegation(RoleDocumentDelegationMember delegationMembershipToCheck, int membershipToCheckIndex, List<RoleDocumentDelegationMember> delegationMemberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) {
529            boolean foundError = false;
530            int count = 0;
531    
532            for (RoleDocumentDelegationMember delegationMembership : delegationMemberships) {
533                    if (membershipToCheckIndex != count) {
534                            if (sameDelegationMembership(delegationMembershipToCheck, delegationMembership)) {
535                                    if (sameUniqueDelegationMembershipQualifications(delegationMembershipToCheck, delegationMembership, uniqueQualifierIds)) {
536                                            foundError = true;
537                                            // add error to each qualifier which is supposed to be unique
538                                            int qualifierCount = 0;
539    
540                                            for (RoleDocumentDelegationMemberQualifier qualifier : delegationMembership.getQualifiers()) {
541                                                    if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) {
542                                                            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());
543                                                    }
544                                                    qualifierCount += 1;
545                                            }
546                                    }
547                            }
548                    }
549                    count += 1;
550            }
551    
552            return foundError;
553        }
554    
555        /**
556         * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id
557         *
558         * @param membershipA the first membership to check
559         * @param membershipB the second membership to check
560         * @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
561         */
562        protected boolean sameDelegationMembership(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB) {
563            if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) {
564                    return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId());
565            }
566            return false;
567        }
568    
569        /**
570         * Given two memberships which represent the same member, do they share qualifications?
571         *
572         * @param membershipA the first membership to check
573         * @param membershipB the second membership to check
574         * @param uniqueAttributeIds the Set of attribute definition ids which should be unique
575         * @return
576         */
577        protected boolean sameUniqueDelegationMembershipQualifications(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB, Set<String> uniqueAttributeIds) {
578            boolean equalSoFar = true;
579            for (String kimAttributeDefinitionId : uniqueAttributeIds) {
580                    final RoleDocumentDelegationMemberQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId);
581                    final RoleDocumentDelegationMemberQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId);
582    
583                    if (qualifierA != null && qualifierB != null) {
584                            equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
585                    }
586            }
587            return equalSoFar;
588        }
589    
590            protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
591                    // TODO : do not have detail bus rule yet, so just check this for now.
592                    boolean valid = true;
593                    if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
594                    MessageMap errorMap = GlobalVariables.getMessageMap();
595                errorMap.putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
596                valid = false;
597    
598                    }
599                    return valid;
600            }
601    
602            /**
603             *
604             * This method checks to see if adding a role to role membership
605             * creates a circular reference.
606             *
607             * @param addMemberEvent
608             * @return true  - ok to assign, no circular references
609             *         false - do not make assignment, will create circular reference.
610             */
611            protected boolean checkForCircularRoleMembership(AddMemberEvent addMemberEvent)
612            {
613                    KimDocumentRoleMember newMember = addMemberEvent.getMember();
614                    if (newMember == null || StringUtils.isBlank(newMember.getMemberId())){
615                            MessageMap errorMap = GlobalVariables.getMessageMap();
616                            errorMap.putError("member.memberId", RiceKeyConstants.ERROR_INVALID_ROLE, new String[] {""});
617                            return false;
618                    }
619                    Set<String> roleMemberIds = null;
620                    // if the role member is a role, check to make sure we won't be creating a circular reference.
621                    // Verify that the new role is not already related to the role either directly or indirectly
622                    if (newMember.isRole()){
623                            // get all nested role member ids that are of type role
624                            RoleService roleService = KimApiServiceLocator.getRoleService();
625                            roleMemberIds = roleService.getRoleTypeRoleMemberIds(newMember.getMemberId());
626    
627                            // check to see if the document role is not a member of the new member role
628                            IdentityManagementRoleDocument document = (IdentityManagementRoleDocument)addMemberEvent.getDocument();
629                            if (roleMemberIds.contains(document.getRoleId())){
630                                    MessageMap errorMap = GlobalVariables.getMessageMap();
631                                    errorMap.putError("member.memberId", RiceKeyConstants.ERROR_ASSIGN_ROLE_MEMBER_CIRCULAR, new String[] {newMember.getMemberId()});
632                                    return false;
633                            }
634                    }
635                    return true;
636            }
637    
638            /**
639             * @return the addResponsibilityRule
640             */
641            public AddResponsibilityRule getAddResponsibilityRule() {
642                    if(addResponsibilityRule == null){
643                            try {
644                                    addResponsibilityRule = addResponsibilityRuleClass.newInstance();
645                            } catch ( Exception ex ) {
646                                    throw new RuntimeException( "Unable to create AddResponsibilityRule instance using class: " + addResponsibilityRuleClass, ex );
647                            }
648                    }
649                    return addResponsibilityRule;
650            }
651    
652            /**
653             * @return the addPermissionRule
654             */
655            public AddPermissionRule getAddPermissionRule() {
656                    if(addPermissionRule == null){
657                            try {
658                                    addPermissionRule = addPermissionRuleClass.newInstance();
659                            } catch ( Exception ex ) {
660                                    throw new RuntimeException( "Unable to create AddPermissionRule instance using class: " + addPermissionRuleClass, ex );
661                            }
662                    }
663                    return addPermissionRule;
664            }
665    
666            /**
667             * @return the addMemberRule
668             */
669            public AddMemberRule getAddMemberRule() {
670                    if(addMemberRule == null){
671                            try {
672                                    addMemberRule = addMemberRuleClass.newInstance();
673                            } catch ( Exception ex ) {
674                                    throw new RuntimeException( "Unable to create AddMemberRule instance using class: " + addMemberRuleClass, ex );
675                            }
676                    }
677                    return addMemberRule;
678            }
679    
680            /**
681             * @return the addDelegationRule
682             */
683            public AddDelegationRule getAddDelegationRule() {
684                    if(addDelegationRule == null){
685                            try {
686                                    addDelegationRule = addDelegationRuleClass.newInstance();
687                            } catch ( Exception ex ) {
688                                    throw new RuntimeException( "Unable to create AddDelegationRule instance using class: " + addDelegationRuleClass, ex );
689                            }
690                    }
691                    return addDelegationRule;
692            }
693    
694            /**
695             * @return the addDelegationMemberRule
696             */
697            public AddDelegationMemberRule getAddDelegationMemberRule() {
698                    if(addDelegationMemberRule == null){
699                            try {
700                                    addDelegationMemberRule = addDelegationMemberRuleClass.newInstance();
701                            } catch ( Exception ex ) {
702                                    throw new RuntimeException( "Unable to create AddDelegationMemberRule instance using class: " + addDelegationMemberRuleClass, ex );
703                            }
704                    }
705                    return addDelegationMemberRule;
706            }
707    
708        public boolean processAddPermission(AddPermissionEvent addPermissionEvent) {
709            return getAddPermissionRule().processAddPermission(addPermissionEvent);
710        }
711    
712        public boolean hasPermissionToGrantPermission(Permission kimPermissionInfo , IdentityManagementRoleDocument document){
713            return getAddPermissionRule().hasPermissionToGrantPermission(kimPermissionInfo, document);
714        }
715    
716        public boolean processAddResponsibility(AddResponsibilityEvent addResponsibilityEvent) {
717            return getAddResponsibilityRule().processAddResponsibility(addResponsibilityEvent);
718        }
719    
720        public boolean hasPermissionToGrantResponsibility(Responsibility kimResponsibilityInfo, IdentityManagementRoleDocument document) {
721            return getAddResponsibilityRule().hasPermissionToGrantResponsibility(kimResponsibilityInfo, document);
722        }
723    
724        public boolean processAddMember(AddMemberEvent addMemberEvent) {
725            boolean success = new KimDocumentMemberRule().processAddMember(addMemberEvent);
726            success &= validateActiveDate("member.activeFromDate", addMemberEvent.getMember().getActiveFromDate(), addMemberEvent.getMember().getActiveToDate());
727            success &= checkForCircularRoleMembership(addMemberEvent);
728            return success;
729        }
730    
731        public boolean processAddDelegation(AddDelegationEvent addDelegationEvent) {
732            return getAddDelegationRule().processAddDelegation(addDelegationEvent);
733        }
734    
735        public boolean processAddDelegationMember(AddDelegationMemberEvent addDelegationMemberEvent) {
736            boolean success = new RoleDocumentDelegationMemberRule().processAddDelegationMember(addDelegationMemberEvent);
737            RoleDocumentDelegationMember roleDocumentDelegationMember = addDelegationMemberEvent.getDelegationMember();
738            success &= validateActiveDate("delegationMember.activeFromDate", roleDocumentDelegationMember.getActiveFromDate(), roleDocumentDelegationMember.getActiveToDate());
739            return success;
740        }
741    
742            public ResponsibilityService getResponsibilityService() {
743                    if(responsibilityService == null){
744                            responsibilityService = KimApiServiceLocator.getResponsibilityService();
745                    }
746                    return responsibilityService;
747            }
748    
749        public ResponsibilityInternalService getResponsibilityInternalService() {
750            if ( responsibilityInternalService == null ) {
751                    responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
752            }
753                    return responsibilityInternalService;
754            }
755    
756    
757            /**
758             * @return the businessObjectService
759             */
760            public BusinessObjectService getBusinessObjectService() {
761                    if(businessObjectService == null){
762                            businessObjectService = KRADServiceLocator.getBusinessObjectService();
763                    }
764                    return businessObjectService;
765            }
766    }