001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.kim.document.rule;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.uif.RemotableAttributeError;
021import org.kuali.rice.core.api.util.RiceKeyConstants;
022import org.kuali.rice.kim.api.identity.IdentityService;
023import org.kuali.rice.kim.api.identity.entity.EntityDefault;
024import org.kuali.rice.kim.api.identity.principal.Principal;
025import org.kuali.rice.kim.api.role.RoleService;
026import org.kuali.rice.kim.api.services.KimApiServiceLocator;
027import org.kuali.rice.kim.api.type.KimAttributeField;
028import org.kuali.rice.kim.api.type.KimType;
029import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
030import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
031import org.kuali.rice.kim.bo.ui.PersonDocumentAffiliation;
032import org.kuali.rice.kim.bo.ui.PersonDocumentBoDefaultBase;
033import org.kuali.rice.kim.bo.ui.PersonDocumentEmploymentInfo;
034import org.kuali.rice.kim.bo.ui.PersonDocumentGroup;
035import org.kuali.rice.kim.bo.ui.PersonDocumentName;
036import org.kuali.rice.kim.bo.ui.PersonDocumentRole;
037import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
038import org.kuali.rice.kim.document.IdentityManagementPersonDocument;
039import org.kuali.rice.kim.document.authorization.IdentityManagementKimDocumentAuthorizer;
040import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
041import org.kuali.rice.kim.framework.type.KimTypeService;
042import org.kuali.rice.kim.impl.KIMPropertyConstants;
043import org.kuali.rice.kim.impl.identity.principal.PrincipalBo;
044import org.kuali.rice.kim.impl.role.RoleMemberBo;
045import org.kuali.rice.kim.impl.type.KimTypeBo;
046import org.kuali.rice.kim.rule.event.ui.AddGroupEvent;
047import org.kuali.rice.kim.rule.event.ui.AddPersonDelegationMemberEvent;
048import org.kuali.rice.kim.rule.event.ui.AddRoleEvent;
049import org.kuali.rice.kim.rule.ui.AddGroupRule;
050import org.kuali.rice.kim.rule.ui.AddPersonDelegationMemberRule;
051import org.kuali.rice.kim.rule.ui.AddPersonDocumentRoleQualifierRule;
052import org.kuali.rice.kim.rule.ui.AddRoleRule;
053import org.kuali.rice.kim.rules.ui.PersonDocumentDelegationMemberRule;
054import org.kuali.rice.kim.rules.ui.PersonDocumentGroupRule;
055import org.kuali.rice.kim.rules.ui.PersonDocumentRoleRule;
056import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
057import org.kuali.rice.kim.service.UiDocumentService;
058import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
059import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
060import org.kuali.rice.krad.document.Document;
061import org.kuali.rice.krad.service.BusinessObjectService;
062import org.kuali.rice.krad.service.KRADServiceLocator;
063import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
064import org.kuali.rice.krad.util.GlobalVariables;
065import org.kuali.rice.krad.util.KRADConstants;
066import org.kuali.rice.krad.util.ObjectUtils;
067
068import java.sql.Timestamp;
069import java.util.ArrayList;
070import java.util.HashMap;
071import java.util.HashSet;
072import java.util.List;
073import java.util.Map;
074import java.util.Set;
075
076/**
077 * This is a description of what this class does - shyu don't forget to fill this in.
078 *
079 * @author Kuali Rice Team (rice.collab@kuali.org)
080 *
081 */
082public class IdentityManagementPersonDocumentRule extends TransactionalDocumentRuleBase implements AddGroupRule, AddRoleRule, AddPersonDocumentRoleQualifierRule, AddPersonDelegationMemberRule {
083
084//      protected static final Logger LOG = Logger.getLogger( IdentityManagementPersonDocumentRule.class );
085
086        protected AddGroupRule addGroupRule;
087        protected AddRoleRule  addRoleRule;
088        protected AddPersonDelegationMemberRule addPersonDelegationMemberRule;
089        protected IdentityManagementKimDocumentAuthorizer authorizer;
090        protected BusinessObjectService businessObjectService;
091        protected IdentityService identityService;
092        protected RoleService roleService;
093        protected UiDocumentService uiDocumentService;
094        protected Class<? extends AddGroupRule> addGroupRuleClass = PersonDocumentGroupRule.class;
095        protected Class<? extends AddRoleRule> addRoleRuleClass = PersonDocumentRoleRule.class;
096        protected Class<? extends AddPersonDelegationMemberRule> addPersonDelegationMemberRuleClass = PersonDocumentDelegationMemberRule.class;
097    protected ActiveRoleMemberHelper activeRoleMemberHelper = new ActiveRoleMemberHelper();
098        protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper();
099
100    @Override
101    protected boolean processCustomSaveDocumentBusinessRules(Document document) {
102        if (!(document instanceof IdentityManagementPersonDocument)) {
103            return false;
104        }
105
106        IdentityManagementPersonDocument personDoc = (IdentityManagementPersonDocument)document;
107        boolean valid = true;
108
109        GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
110
111        //KRADServiceLocatorInternal.getDictionaryValidationService().validateDocument(document);
112        getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false);
113        valid &= validDuplicatePrincipalName(personDoc);
114        EntityDefault origEntity = getIdentityService().getEntityDefault(personDoc.getEntityId());
115        boolean isCreatingNew = origEntity==null?true:false;
116        if(getUIDocumentService().canModifyEntity(GlobalVariables.getUserSession().getPrincipalId(), personDoc.getPrincipalId()) || isCreatingNew) {
117                valid &= validateEntityInformation(isCreatingNew, personDoc);
118        }
119        // kimtypeservice.validateAttributes is not working yet.
120        valid &= validateRoleQualifier (personDoc.getRoles());
121        valid &= validateDelegationMemberRoleQualifier(personDoc.getDelegationMembers());
122        if (StringUtils.isNotBlank(personDoc.getPrincipalName())) {
123                valid &= doesPrincipalNameExist (personDoc.getPrincipalName(), personDoc.getPrincipalId());
124        }
125
126        valid &= validActiveDatesForRole (personDoc.getRoles());
127        valid &= validActiveDatesForGroup (personDoc.getGroups());
128        valid &= validActiveDatesForDelegations (personDoc.getDelegationMembers());
129
130
131        // all failed at this point.
132//        valid &= checkUnassignableRoles(personDoc);
133//        valid &= checkUnpopulatableGroups(personDoc);
134
135        GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
136
137        return valid;
138    }
139
140    protected boolean validateEntityInformation(boolean isCreatingNew, IdentityManagementPersonDocument personDoc){
141        boolean valid = true;
142        boolean canOverridePrivacyPreferences = getUIDocumentService().canOverrideEntityPrivacyPreferences(GlobalVariables.getUserSession().getPrincipalId(), personDoc.getPrincipalId());
143        valid &= checkMultipleDefault (personDoc.getAffiliations(), "affiliations");
144        if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressName()) {
145                valid &= checkMultipleDefault (personDoc.getNames(), "names");
146        }
147        if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressAddress()) {
148                valid &= checkMultipleDefault (personDoc.getAddrs(), "addrs");
149        }
150        if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressPhone()) {
151                valid &= checkMultipleDefault (personDoc.getPhones(), "phones");
152        }
153        if(isCreatingNew || canOverridePrivacyPreferences || !personDoc.getPrivacy().isSuppressEmail()) {
154                valid &= checkMultipleDefault (personDoc.getEmails(), "emails");
155        }
156        valid &= checkPrimaryEmploymentInfo (personDoc.getAffiliations());
157        valid &= validEmployeeIDForAffiliation(personDoc.getAffiliations());
158        valid &= checkAffiliationTypeChange (personDoc.getAffiliations());
159        valid &= checkUniqueAffiliationTypePerCampus(personDoc.getAffiliations());
160        return valid;
161    }
162
163    @SuppressWarnings("unchecked")
164        protected boolean validDuplicatePrincipalName(IdentityManagementPersonDocument personDoc){
165        Map<String, String> criteria = new HashMap<String, String>();
166        criteria.put("principalName", personDoc.getPrincipalName());
167        List<PrincipalBo> prncplImpls = (List<PrincipalBo>)getBusinessObjectService().findMatching(PrincipalBo.class, criteria);
168        boolean rulePassed = true;
169        if(prncplImpls!=null && prncplImpls.size()>0){
170                if(prncplImpls.size()==1 && prncplImpls.get(0).getPrincipalId().equals(personDoc.getPrincipalId())) {
171                        rulePassed = true;
172            }
173                else{
174                        GlobalVariables.getMessageMap().putError("document.principalName",
175                                        RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Principal Name"});
176                        rulePassed = false;
177                }
178        }
179        return rulePassed;
180    }
181
182        protected boolean checkUnassignableRoles(IdentityManagementPersonDocument document) {
183                boolean valid = true;
184        Map<String,Set<String>> unassignableRoles = getAuthorizer( document ).getUnassignableRoles(document, GlobalVariables.getUserSession().getPerson());
185        for (String namespaceCode : unassignableRoles.keySet()) {
186                for (String roleName : unassignableRoles.get(namespaceCode)) {
187                        int i = 0;
188                        for (PersonDocumentRole role : document.getRoles()) {
189                                if (role.isEditable() && namespaceCode.endsWith(role.getNamespaceCode()) && roleName.equals(role.getRoleName())) {
190                                        GlobalVariables.getMessageMap().putError("roles["+i+"].roleId", RiceKeyConstants.ERROR_ASSIGN_ROLE, new String[] {namespaceCode, roleName});
191                                valid = false;
192                                }
193                                i++;
194                        }
195                }
196        }
197        return valid;
198        }
199
200        protected boolean checkUnpopulatableGroups(IdentityManagementPersonDocument document) {
201                boolean valid = true;
202        Map<String,Set<String>> unpopulatableGroups = getAuthorizer( document ).getUnpopulateableGroups(document, GlobalVariables.getUserSession().getPerson());
203        for (String namespaceCode : unpopulatableGroups.keySet()) {
204                for (String groupName : unpopulatableGroups.get(namespaceCode)) {
205                        int i = 0;
206                        for (PersonDocumentGroup group : document.getGroups()) {
207                                if ( (group.getNamespaceCode() != null && namespaceCode.endsWith(group.getNamespaceCode())) && (group.getGroupName() != null && groupName.equals(group.getGroupName()))) {
208                                        GlobalVariables.getMessageMap().putError("groups["+i+"].groupId", RiceKeyConstants.ERROR_POPULATE_GROUP, new String[] {namespaceCode, groupName});
209                                }
210                                i++;
211                        }
212                }
213                valid = false;
214        }
215        return valid;
216        }
217
218    @Override
219        protected boolean processCustomRouteDocumentBusinessRules(Document document) {
220                super.processCustomRouteDocumentBusinessRules(document);
221        IdentityManagementPersonDocument personDoc = (IdentityManagementPersonDocument)document;
222        boolean valid = true;
223        GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
224        valid &= validateAffiliationAndName( personDoc );
225        valid &= checkAffiliationEithOneEMpInfo (personDoc.getAffiliations());
226        GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
227
228        return valid;
229        }
230
231
232        protected boolean checkMultipleDefault (List <? extends PersonDocumentBoDefaultBase> boList, String listName) {
233        boolean valid = true;
234        boolean isDefaultSet = false;
235        int i = 0;
236        for (PersonDocumentBoDefaultBase item : boList) {
237                if (item.isDflt()) {
238                        if (isDefaultSet) {
239                                GlobalVariables.getMessageMap().putError(listName+"[" + i + "].dflt",RiceKeyConstants.ERROR_MULTIPLE_DEFAULT_SELETION);
240                                valid = false;
241                        } else {
242                                isDefaultSet = true;
243                        }
244                }
245                i++;
246        }
247        if (!boList.isEmpty() && !isDefaultSet) {
248                                GlobalVariables.getMessageMap().putError(listName+"[0].dflt",RiceKeyConstants.ERROR_NO_DEFAULT_SELETION);
249        }
250        return valid;
251    }
252
253    protected boolean checkPrimaryEmploymentInfo (List <PersonDocumentAffiliation> affiliations) {
254        boolean valid = true;
255        int i = 0;
256        int firstAfflnCounter = -1;
257        boolean isPrimarySet = false;
258        for (PersonDocumentAffiliation affiliation : affiliations) {
259                int j = 0;
260                for (PersonDocumentEmploymentInfo empInfo : affiliation.getEmpInfos()) {
261                        if(firstAfflnCounter==-1) {
262                                firstAfflnCounter = i;
263                }
264                        if (empInfo.isPrimary()) {
265                                if (isPrimarySet) {
266                                        // primary per principal or primary per affiliation ?
267                                        GlobalVariables.getMessageMap().putError("affiliations[" + i + "].empInfos["+ j +"].primary",RiceKeyConstants.ERROR_MULTIPLE_PRIMARY_EMPLOYMENT);
268                                        valid = false;
269                                } else {
270                                        isPrimarySet = true;
271                                }
272                                j++;
273                        }
274                }
275                i++;
276        }
277        if(!isPrimarySet && firstAfflnCounter!=-1){
278                GlobalVariables.getMessageMap().putError("affiliations[" + firstAfflnCounter + "].empInfos[0].primary",RiceKeyConstants.ERROR_NO_PRIMARY_EMPLOYMENT);
279                valid = false;
280        }
281        return valid;
282    }
283
284    protected boolean checkAffiliationTypeChange (List <PersonDocumentAffiliation> affiliations) {
285        boolean valid = true;
286        int i = 0;
287        for (PersonDocumentAffiliation affiliation : affiliations) {
288                if (affiliation.getAffiliationType() != null && !affiliation.getAffiliationTypeCode().equals(affiliation.getAffiliationType().getCode())) {
289                        PersonDocumentAffiliation copiedAffiliation = (PersonDocumentAffiliation)ObjectUtils.deepCopy(affiliation);
290                        copiedAffiliation.refreshReferenceObject("affiliationType");
291                        if (!copiedAffiliation.getAffiliationType().isEmploymentAffiliationType() && affiliation.getAffiliationType().isEmploymentAffiliationType() && !copiedAffiliation.getEmpInfos().isEmpty()) {
292                                GlobalVariables.getMessageMap().putError("affiliations[" + i + "].affiliationTypeCode",RiceKeyConstants.ERROR_NOT_EMPLOYMENT_AFFILIATION_TYPE,new String[] {affiliation.getAffiliationType().getName(), copiedAffiliation.getAffiliationType().getName()});
293                                valid = false;
294                        }
295                }
296                i++;
297        }
298        return valid;
299    }
300
301    protected boolean validEmployeeIDForAffiliation(List <PersonDocumentAffiliation> affiliations) {
302        boolean valid = true;
303        int i = 0;
304        int j = 0;
305        for(PersonDocumentAffiliation affiliation : affiliations) {
306                if(affiliation.getAffiliationType() != null && affiliation.getAffiliationType().isEmploymentAffiliationType()){
307                        if(affiliation.getEmpInfos()!=null){
308                        j = 0;
309                        for (PersonDocumentEmploymentInfo empInfo : affiliation.getEmpInfos()) {
310                                if (StringUtils.isEmpty(empInfo.getEmployeeId())) {
311                                                GlobalVariables.getMessageMap().putError("affiliations[" + i + "].empInfos["+ j +"].employeeId", RiceKeyConstants.ERROR_REQUIRED_CONDITIONALLY, new String[] {"Employee ID", "an employee"});
312                                                valid = false;
313                                        j++;
314                                }
315                        }
316                        }
317                }
318                i++;
319        }
320        return valid;
321    }
322
323    protected boolean isPersonAnEmployee(List<PersonDocumentAffiliation> affiliations){
324        boolean isEmployee = false;
325        for (PersonDocumentAffiliation affiliation : affiliations){
326                if (affiliation.getAffiliationType() != null && affiliation.getAffiliationType().isEmploymentAffiliationType()){
327                        isEmployee = true;
328                        break;
329                }
330        }
331        return isEmployee;
332    }
333
334    protected boolean checkUniqueAffiliationTypePerCampus (List <PersonDocumentAffiliation> affiliations) {
335        boolean valid = true;
336        int i = 0;
337        for (PersonDocumentAffiliation affiliation : affiliations) {
338                int j = 0;
339                for (PersonDocumentAffiliation affiliation1 : affiliations) {
340                        if (j > i && affiliation.getAffiliationTypeCode() .equals(affiliation1.getAffiliationTypeCode()) && affiliation.getCampusCode().equals(affiliation1.getCampusCode())) {
341                                        GlobalVariables.getMessageMap().putError("affiliations[" + j + "].affiliationTypeCode",RiceKeyConstants.ERROR_NOT_UNIQUE_AFFILIATION_TYPE_PER_CAMPUE, affiliation.getAffiliationType().getName());
342                                        valid = false;
343                        }
344                        j++;
345                }
346                i++;
347        }
348        return valid;
349    }
350
351    protected boolean checkAffiliationEithOneEMpInfo (List <PersonDocumentAffiliation> affiliations) {
352        boolean valid = true;
353        int i = 0;
354        for (PersonDocumentAffiliation affiliation : affiliations) {
355                        if (affiliation.getAffiliationType() .isEmploymentAffiliationType() && affiliation.getEmpInfos().isEmpty()) {
356                                        GlobalVariables.getMessageMap().putError("affiliations[" + i + "].affiliationTypeCode",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "Employment Information");
357                                        valid = false;
358                        }
359                i++;
360        }
361        return valid;
362    }
363
364    /*
365     * Verify at least one affiliation and one default name
366     */
367    protected boolean validateAffiliationAndName(IdentityManagementPersonDocument personDoc) {
368        boolean valid = true;
369        if (personDoc.getAffiliations().isEmpty()) {
370                GlobalVariables.getMessageMap().putError("affiliations[0]",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "affiliation");
371                valid = false;
372        }
373        if (personDoc.getNames().isEmpty()) {
374                GlobalVariables.getMessageMap().putError("names[0]",RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "name");
375                valid = false;
376        } else{
377                boolean activeExists = false;
378                for(PersonDocumentName name: personDoc.getNames()){
379                if(name.isActive()){
380                        activeExists = true;
381                        }
382                }
383                if(!activeExists){
384                        GlobalVariables.getMessageMap().putError("names[0]", RiceKeyConstants.ERROR_ONE_ACTIVE_ITEM_REQUIRED, "name");
385                        valid = false;
386                }
387                return valid;
388
389        }
390        return valid;
391    }
392
393    protected boolean doesPrincipalNameExist (String principalName, String principalId) {
394        Principal principal = getIdentityService().getPrincipalByPrincipalName(principalName);
395        if (principal != null && (StringUtils.isBlank(principalId) || !principal.getPrincipalId().equals(principalId))) {
396                GlobalVariables.getMessageMap().putError(KIMPropertyConstants.Person.PRINCIPAL_NAME,RiceKeyConstants.ERROR_EXIST_PRINCIPAL_NAME, principalName);
397                        return false;
398        }
399        return true;
400    }
401
402    protected boolean validateRoleQualifier( List<PersonDocumentRole> roles ) {
403                List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
404        GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
405        int i = 0;
406        for(PersonDocumentRole role : roles ) {
407                KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to(
408                    role.getKimRoleType()));
409                if(CollectionUtils.isEmpty(role.getRolePrncpls()) && !role.getDefinitions().isEmpty()){
410                        KimType kimTypeInfo = KimApiServiceLocator.getKimTypeInfoService().getKimType(role.getKimRoleType().getId());
411                        Map<String, String> blankQualifiers = attributeValidationHelper.getBlankValueQualifiersMap(kimTypeInfo.getAttributeDefinitions());
412                        List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes(
413                                role.getKimRoleType().getId(), blankQualifiers);
414                        if(localErrors!=null && !localErrors.isEmpty()){
415                                GlobalVariables.getMessageMap().putError("document.roles["+i+"].newRolePrncpl.qualifiers[0].attrVal",
416                                                RiceKeyConstants.ERROR_ONE_ITEM_REQUIRED, "Role Qualifier");
417                                return false;
418                        }
419                }
420
421                final List<KimAttributeField> attributeDefinitions = role.getDefinitions();
422                final Set<String> uniqueQualifierAttributes = findUniqueQualificationAttributes(role, attributeDefinitions);
423
424                if ( kimTypeService != null ) {
425                        int j = 0;
426
427                        for ( KimDocumentRoleMember rolePrincipal : activeRoleMemberHelper.getActiveRoleMembers(role.getRolePrncpls()) ) {
428                                List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), attributeValidationHelper.convertQualifiersToMap( rolePrincipal.getQualifiers() ) );
429                                validationErrors.addAll( attributeValidationHelper.convertErrors(
430                            "roles[" + i + "].rolePrncpls[" + j + "]",
431                            attributeValidationHelper.convertQualifiersToAttrIdxMap(rolePrincipal.getQualifiers()),
432                            localErrors));
433
434                                if (uniqueQualifierAttributes.size() > 0) {
435                                        validateUniquePersonRoleQualifiersUniqueForMembership(role, rolePrincipal, j, uniqueQualifierAttributes, i, validationErrors);
436                                }
437
438                                j++;
439                        }
440                }
441                i++;
442        }
443        GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
444        if (validationErrors.isEmpty()) {
445                return true;
446        } else {
447                attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
448                return false;
449        }
450    }
451
452    /**
453     * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
454     *
455     * @param roleIndex the index of the role on the document (for error reporting purposes)
456     * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
457     * @return true if all unique values are indeed unique, false otherwise
458     */
459    protected boolean validateUniquePersonRoleQualifiersUniqueForMembership(PersonDocumentRole role, KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, Set<String> uniqueQualifierAttributes, int roleIndex, List<RemotableAttributeError> validationErrors) {
460        boolean foundError = false;
461        int count = 0;
462
463        for (KimDocumentRoleMember membership : role.getRolePrncpls()) {
464            if (sameMembershipQualifications(membershipToCheck, membership, uniqueQualifierAttributes)) {
465                if (count == 0 ) {
466                    count +=1;
467                } else {
468                    count += 1;
469                    foundError = true;
470
471                            int qualifierCount = 0;
472
473                            for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
474                                    if (qualifier != null && uniqueQualifierAttributes.contains(qualifier.getKimAttrDefnId())) {
475                                                    validationErrors.add(RemotableAttributeError.Builder.create("document.roles["+roleIndex+"].rolePrncpls["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+qualifier.getKimAttribute().getAttributeName()+";"+qualifier.getAttrVal()).build());
476                                            }
477                                        qualifierCount += 1;
478                                    }
479                }
480                }
481        }
482        return foundError;
483    }
484
485    /**
486     * Determines if two seperate memberships have the same qualifications
487     * @param membershipA the first membership to check
488     * @param membershipB the second membership to check
489     * @param uniqueQualifierAttributes the set of qualifier attributes which need to be unique
490     * @return true if equal, false if otherwise
491     */
492    protected boolean sameMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueQualifierAttributes) {
493        boolean equalSoFar = true;
494        for (String uniqueQualifierAttributeDefinitionId : uniqueQualifierAttributes) {
495                final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(uniqueQualifierAttributeDefinitionId);
496                final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(uniqueQualifierAttributeDefinitionId);
497
498                if (qualifierA != null && qualifierB != null) {
499                        equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
500                }
501        }
502        return equalSoFar;
503    }
504
505    /**
506     * Finds the set of unique qualification attributes for the given role
507     *
508     * @param role the role associated with this person
509     * @param attributeDefinitions the Map of attribute definitions where we can find out if a KimAttribute is supposed to be unique
510     * @return a Set of attribute definition ids for qualifications which are supposed to be unique
511     */
512    public Set<String> findUniqueQualificationAttributes(PersonDocumentRole role, List<KimAttributeField> attributeDefinitions) {
513        Set<String> uniqueQualifications = new HashSet<String>();
514
515        if (role.getRolePrncpls() != null && role.getRolePrncpls().size() > 1) {
516                final KimDocumentRoleMember membership = role.getRolePrncpls().get(0);
517                for (KimDocumentRoleQualifier qualifier: membership.getQualifiers()) {
518                        if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
519                        final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
520                            qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
521
522                        if (relatedDefinition != null && relatedDefinition.isUnique()) {
523                                uniqueQualifications.add(qualifier.getKimAttrDefnId());
524                        }
525                        }
526                }
527        }
528
529        return uniqueQualifications;
530    }
531
532    protected boolean validActiveDatesForRole (List<PersonDocumentRole> roles ) {
533        boolean valid = true;
534                int i = 0;
535        for(PersonDocumentRole role : roles ) {
536                        int j = 0;
537                for (KimDocumentRoleMember principal : role.getRolePrncpls()) {
538                        valid &= validateActiveDate("roles["+i+"].rolePrncpls["+j+"].activeToDate",principal.getActiveFromDate(), principal.getActiveToDate());
539                        j++;
540                }
541                i++;
542        }
543        return valid;
544    }
545
546    protected boolean validActiveDatesForGroup (List<PersonDocumentGroup> groups ) {
547        boolean valid = true;
548                int i = 0;
549        for(PersonDocumentGroup group : groups ) {
550                valid &= validateActiveDate("groups["+i+"].activeToDate",group.getActiveFromDate(), group.getActiveToDate());
551                i++;
552        }
553        return valid;
554    }
555
556    protected boolean validActiveDatesForDelegations(List<RoleDocumentDelegationMember> delegationMembers) {
557        boolean valid = true;
558                int i = 0;
559                for(RoleDocumentDelegationMember delegationMember: delegationMembers){
560                valid &= validateActiveDate("delegationMembers["+i+"].activeToDate", delegationMember.getActiveFromDate(), delegationMember.getActiveToDate());
561                i++;
562                }
563        return valid;
564    }
565
566        protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
567                // TODO : do not have detail bus rule yet, so just check this for now.
568                boolean valid = true;
569                if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
570                GlobalVariables.getMessageMap().putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
571            valid = false;
572                }
573                return valid;
574        }
575
576    public boolean processAddGroup(AddGroupEvent addGroupEvent) {
577        return getAddGroupRule().processAddGroup(addGroupEvent);
578    }
579
580    public boolean processAddRole(AddRoleEvent addRoleEvent) {
581        return getAddRoleRule().processAddRole(addRoleEvent);
582    }
583
584    public boolean processAddPersonDelegationMember(AddPersonDelegationMemberEvent addPersonDelegationMemberEvent){
585        return getAddPersonDelegationMemberRule().processAddPersonDelegationMember(addPersonDelegationMemberEvent);
586    }
587
588        public IdentityService getIdentityService() {
589                if ( identityService == null ) {
590                        identityService = KimApiServiceLocator.getIdentityService();
591                }
592                return identityService;
593        }
594
595        public RoleService getRoleService() {
596                if ( roleService == null ) {
597                        roleService = KimApiServiceLocator.getRoleService();
598                }
599                return roleService;
600        }
601
602        public UiDocumentService getUIDocumentService() {
603                if ( uiDocumentService == null ) {
604                        uiDocumentService = KIMServiceLocatorInternal.getUiDocumentService();
605                }
606                return uiDocumentService;
607        }
608
609        public IdentityManagementKimDocumentAuthorizer getAuthorizer(IdentityManagementPersonDocument document) {
610                if ( authorizer == null ) {
611                        authorizer = (IdentityManagementKimDocumentAuthorizer) KRADServiceLocatorWeb.getDocumentDictionaryService().getDocumentAuthorizer(document);
612                }
613                return authorizer;
614        }
615
616
617
618        /**
619         * @return the addGroupRuleClass
620         */
621        public Class<? extends AddGroupRule> getAddGroupRuleClass() {
622                return this.addGroupRuleClass;
623        }
624
625
626
627        /**
628         * Can be overridden by subclasses to indicate the rule class to use when adding groups.
629         *
630         * @param addGroupRuleClass the addGroupRuleClass to set
631         */
632        public void setAddGroupRuleClass(Class<? extends AddGroupRule> addGroupRuleClass) {
633                this.addGroupRuleClass = addGroupRuleClass;
634        }
635
636
637
638        /**
639         * @return the addRoleRuleClass
640         */
641        public Class<? extends AddRoleRule> getAddRoleRuleClass() {
642                return this.addRoleRuleClass;
643        }
644
645
646
647        /**
648         * Can be overridden by subclasses to indicate the rule class to use when adding roles.
649         *
650         * @param addRoleRuleClass the addRoleRuleClass to set
651         */
652        public void setAddRoleRuleClass(Class<? extends AddRoleRule> addRoleRuleClass) {
653                this.addRoleRuleClass = addRoleRuleClass;
654        }
655
656
657
658        /**
659         * @return the addGroupRule
660         */
661        public AddGroupRule getAddGroupRule() {
662                if ( addGroupRule == null ) {
663                        try {
664                                addGroupRule = addGroupRuleClass.newInstance();
665                        } catch ( Exception ex ) {
666                                throw new RuntimeException( "Unable to create AddGroupRule instance using class: " + addGroupRuleClass, ex );
667                        }
668                }
669                return addGroupRule;
670        }
671
672
673
674        /**
675         * @return the addRoleRule
676         */
677        public AddRoleRule getAddRoleRule() {
678                if ( addRoleRule == null ) {
679                        try {
680                                addRoleRule = addRoleRuleClass.newInstance();
681                        } catch ( Exception ex ) {
682                                throw new RuntimeException( "Unable to create AddRoleRule instance using class: " + addRoleRuleClass, ex );
683                        }
684                }
685                return addRoleRule;
686        }
687
688        /**
689         * @return the addRoleRule
690         */
691        public AddPersonDelegationMemberRule getAddPersonDelegationMemberRule() {
692                if(addPersonDelegationMemberRule == null){
693                        try {
694                                addPersonDelegationMemberRule = addPersonDelegationMemberRuleClass.newInstance();
695                        } catch ( Exception ex ) {
696                                throw new RuntimeException( "Unable to create AddPersonDelegationMemberRuleClass instance using class: " + addPersonDelegationMemberRuleClass, ex );
697                        }
698                }
699                return addPersonDelegationMemberRule;
700        }
701
702        /**
703         * @return the businessObjectService
704         */
705        public BusinessObjectService getBusinessObjectService() {
706                if ( businessObjectService == null ) {
707                        businessObjectService = KRADServiceLocator.getBusinessObjectService();
708                }
709                return businessObjectService;
710        }
711
712        public boolean processAddPersonDocumentRoleQualifier(IdentityManagementPersonDocument document, PersonDocumentRole role, KimDocumentRoleMember kimDocumentRoleMember, int selectedRoleIdx) {
713                boolean dateValidationSuccess = validateActiveDate("document.roles[" + selectedRoleIdx + "].newRolePrncpl.activeFromDate", kimDocumentRoleMember.getActiveFromDate(), kimDocumentRoleMember.getActiveToDate());
714                String errorPath = "roles[" + selectedRoleIdx + "].newRolePrncpl";
715                List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
716        GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
717        KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to(role.getKimRoleType()));
718
719                List<RemotableAttributeError> errorsAttributesAgainstExisting;
720            int i = 0;
721            boolean rulePassed = true;
722            Map<String, String> newMemberQualifiers = attributeValidationHelper.convertQualifiersToMap(kimDocumentRoleMember.getQualifiers());
723            Map<String, String> oldMemberQualifiers;
724            List<String> roleIds = new ArrayList<String>();
725            roleIds.add(role.getRoleId());
726            for(KimDocumentRoleMember member: role.getRolePrncpls()){
727                oldMemberQualifiers = member.getQualifierAsMap();
728                errorsAttributesAgainstExisting = kimTypeService.validateUniqueAttributes(
729                                role.getKimRoleType().getId(), newMemberQualifiers, oldMemberQualifiers);
730                validationErrors.addAll(
731                                        attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
732                            .convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
733                            errorsAttributesAgainstExisting));
734                i++;
735            }
736
737        if ( kimTypeService != null ) {
738                List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), newMemberQualifiers );
739            validationErrors.addAll( attributeValidationHelper.convertErrors(errorPath,
740                    attributeValidationHelper.convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
741                    localErrors));
742        }
743
744        GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
745        if (validationErrors.isEmpty()) {
746                rulePassed = dateValidationSuccess;
747        } else {
748                attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
749                rulePassed = false;
750        }
751        return rulePassed;
752        }
753
754    protected boolean validateDelegationMemberRoleQualifier(List<RoleDocumentDelegationMember> delegationMembers){
755                List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
756                boolean valid;
757                int memberCounter = 0;
758                List<RemotableAttributeError> errorsTemp;
759                Map<String, String> mapToValidate;
760        KimTypeService kimTypeService;
761        GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
762        RoleMemberBo roleMember;
763        String errorPath;
764        ArrayList<String> roleIds;
765        KimType kimType;
766                for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
767                        kimType = KimTypeBo.to(delegationMember.getRoleBo().getKimRoleType());
768                        kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
769                        roleIds = new ArrayList<String>();
770                        roleIds.add(delegationMember.getRoleBo().getId());
771                        errorPath = "delegationMembers["+memberCounter+"]";
772                        mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers());
773                        errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
774                        validationErrors.addAll(
775                                        attributeValidationHelper.convertErrors(errorPath,
776                            attributeValidationHelper.convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()),
777                            errorsTemp));
778
779                        roleMember = getUIDocumentService().getRoleMember(delegationMember.getRoleMemberId());
780                        if(roleMember==null){
781                                valid = false;
782                                GlobalVariables.getMessageMap().putError("document."+errorPath, RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{});
783                        } else{
784                                kimTypeService.validateUnmodifiableAttributes(kimType.getId(), roleMember.getAttributes(), mapToValidate);
785                                validationErrors.addAll(
786                                                attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
787                                .convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()), errorsTemp));
788                        }
789                memberCounter++;
790        }
791                GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
792        if (validationErrors.isEmpty()) {
793                valid = true;
794        } else {
795                attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
796                valid = false;
797        }
798        return valid;
799    }
800
801}