001    /**
002     * Copyright 2005-2014 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.collections.CollectionUtils;
019    import org.apache.commons.lang.StringUtils;
020    import org.kuali.rice.core.api.uif.RemotableAttributeError;
021    import org.kuali.rice.core.api.util.RiceKeyConstants;
022    import org.kuali.rice.kim.api.identity.IdentityService;
023    import org.kuali.rice.kim.api.identity.entity.EntityDefault;
024    import org.kuali.rice.kim.api.identity.principal.Principal;
025    import org.kuali.rice.kim.api.role.RoleService;
026    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
027    import org.kuali.rice.kim.api.type.KimAttributeField;
028    import org.kuali.rice.kim.api.type.KimType;
029    import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
030    import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
031    import org.kuali.rice.kim.bo.ui.PersonDocumentAffiliation;
032    import org.kuali.rice.kim.bo.ui.PersonDocumentBoDefaultBase;
033    import org.kuali.rice.kim.bo.ui.PersonDocumentEmploymentInfo;
034    import org.kuali.rice.kim.bo.ui.PersonDocumentGroup;
035    import org.kuali.rice.kim.bo.ui.PersonDocumentName;
036    import org.kuali.rice.kim.bo.ui.PersonDocumentRole;
037    import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
038    import org.kuali.rice.kim.document.IdentityManagementPersonDocument;
039    import org.kuali.rice.kim.document.authorization.IdentityManagementKimDocumentAuthorizer;
040    import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
041    import org.kuali.rice.kim.framework.type.KimTypeService;
042    import org.kuali.rice.kim.impl.KIMPropertyConstants;
043    import org.kuali.rice.kim.impl.identity.principal.PrincipalBo;
044    import org.kuali.rice.kim.impl.role.RoleMemberBo;
045    import org.kuali.rice.kim.impl.type.KimTypeBo;
046    import org.kuali.rice.kim.rule.event.ui.AddGroupEvent;
047    import org.kuali.rice.kim.rule.event.ui.AddPersonDelegationMemberEvent;
048    import org.kuali.rice.kim.rule.event.ui.AddRoleEvent;
049    import org.kuali.rice.kim.rule.ui.AddGroupRule;
050    import org.kuali.rice.kim.rule.ui.AddPersonDelegationMemberRule;
051    import org.kuali.rice.kim.rule.ui.AddPersonDocumentRoleQualifierRule;
052    import org.kuali.rice.kim.rule.ui.AddRoleRule;
053    import org.kuali.rice.kim.rules.ui.PersonDocumentDelegationMemberRule;
054    import org.kuali.rice.kim.rules.ui.PersonDocumentGroupRule;
055    import org.kuali.rice.kim.rules.ui.PersonDocumentRoleRule;
056    import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
057    import org.kuali.rice.kim.service.UiDocumentService;
058    import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
059    import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
060    import org.kuali.rice.krad.document.Document;
061    import org.kuali.rice.krad.service.BusinessObjectService;
062    import org.kuali.rice.krad.service.KRADServiceLocator;
063    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
064    import org.kuali.rice.krad.util.GlobalVariables;
065    import org.kuali.rice.krad.util.KRADConstants;
066    import org.kuali.rice.krad.util.ObjectUtils;
067    
068    import java.sql.Timestamp;
069    import java.util.ArrayList;
070    import java.util.HashMap;
071    import java.util.HashSet;
072    import java.util.List;
073    import java.util.Map;
074    import 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     */
082    public 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    }