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.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.krad.document.Document;
060    import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase;
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    
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                            for ( KimDocumentRoleMember rolePrincipal : role.getRolePrncpls() ) {
427                                    List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), attributeValidationHelper.convertQualifiersToMap( rolePrincipal.getQualifiers() ) );
428                                    validationErrors.addAll( attributeValidationHelper.convertErrors(
429                                "roles[" + i + "].rolePrncpls[" + j + "]",
430                                attributeValidationHelper.convertQualifiersToAttrIdxMap(rolePrincipal.getQualifiers()),
431                                localErrors));
432    
433                                    if (uniqueQualifierAttributes.size() > 0) {
434                                            validateUniquePersonRoleQualifiersUniqueForMembership(role, rolePrincipal, j, uniqueQualifierAttributes, i, validationErrors);
435                                    }
436    
437                                    j++;
438                            }
439                    }
440                    i++;
441            }
442            GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
443            if (validationErrors.isEmpty()) {
444                    return true;
445            } else {
446                    attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
447                    return false;
448            }
449        }
450    
451        /**
452         * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique
453         *
454         * @param roleIndex the index of the role on the document (for error reporting purposes)
455         * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes)
456         * @return true if all unique values are indeed unique, false otherwise
457         */
458        protected boolean validateUniquePersonRoleQualifiersUniqueForMembership(PersonDocumentRole role, KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, Set<String> uniqueQualifierAttributes, int roleIndex, List<RemotableAttributeError> validationErrors) {
459            boolean foundError = false;
460            int count = 0;
461    
462            for (KimDocumentRoleMember membership : role.getRolePrncpls()) {
463                    if (membershipToCheckIndex != count) {
464                            if (sameMembershipQualifications(membershipToCheck, membership, uniqueQualifierAttributes)) {
465                                    foundError = true;
466    
467                                    int qualifierCount = 0;
468    
469                                            for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) {
470                                                    if (qualifier != null && uniqueQualifierAttributes.contains(qualifier.getKimAttrDefnId())) {
471                                                            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());
472                                                    }
473                                                    qualifierCount += 1;
474                                            }
475                            }
476                    }
477    
478                    count += 1;
479            }
480            return foundError;
481        }
482    
483        /**
484         * Determines if two seperate memberships have the same qualifications
485         * @param membershipA the first membership to check
486         * @param membershipB the second membership to check
487         * @param uniqueQualifierAttributes the set of qualifier attributes which need to be unique
488         * @return true if equal, false if otherwise
489         */
490        protected boolean sameMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueQualifierAttributes) {
491            boolean equalSoFar = true;
492            for (String uniqueQualifierAttributeDefinitionId : uniqueQualifierAttributes) {
493                    final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(uniqueQualifierAttributeDefinitionId);
494                    final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(uniqueQualifierAttributeDefinitionId);
495    
496                    if (qualifierA != null && qualifierB != null) {
497                            equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal()));
498                    }
499            }
500            return equalSoFar;
501        }
502    
503        /**
504         * Finds the set of unique qualification attributes for the given role
505         *
506         * @param role the role associated with this person
507         * @param attributeDefinitions the Map of attribute definitions where we can find out if a KimAttribute is supposed to be unique
508         * @return a Set of attribute definition ids for qualifications which are supposed to be unique
509         */
510        public Set<String> findUniqueQualificationAttributes(PersonDocumentRole role, List<KimAttributeField> attributeDefinitions) {
511            Set<String> uniqueQualifications = new HashSet<String>();
512    
513            if (role.getRolePrncpls() != null && role.getRolePrncpls().size() > 1) {
514                    final KimDocumentRoleMember membership = role.getRolePrncpls().get(0);
515                    for (KimDocumentRoleQualifier qualifier: membership.getQualifiers()) {
516                            if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) {
517                            final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField(
518                                qualifier.getKimAttribute().getAttributeName(), attributeDefinitions);
519    
520                            if (relatedDefinition != null && relatedDefinition.isUnique()) {
521                                    uniqueQualifications.add(qualifier.getKimAttrDefnId());
522                            }
523                            }
524                    }
525            }
526    
527            return uniqueQualifications;
528        }
529    
530        protected boolean validActiveDatesForRole (List<PersonDocumentRole> roles ) {
531            boolean valid = true;
532                    int i = 0;
533            for(PersonDocumentRole role : roles ) {
534                            int j = 0;
535                    for (KimDocumentRoleMember principal : role.getRolePrncpls()) {
536                            valid &= validateActiveDate("roles["+i+"].rolePrncpls["+j+"].activeToDate",principal.getActiveFromDate(), principal.getActiveToDate());
537                            j++;
538                    }
539                    i++;
540            }
541            return valid;
542        }
543    
544        protected boolean validActiveDatesForGroup (List<PersonDocumentGroup> groups ) {
545            boolean valid = true;
546                    int i = 0;
547            for(PersonDocumentGroup group : groups ) {
548                    valid &= validateActiveDate("groups["+i+"].activeToDate",group.getActiveFromDate(), group.getActiveToDate());
549                    i++;
550            }
551            return valid;
552        }
553    
554        protected boolean validActiveDatesForDelegations(List<RoleDocumentDelegationMember> delegationMembers) {
555            boolean valid = true;
556                    int i = 0;
557                    for(RoleDocumentDelegationMember delegationMember: delegationMembers){
558                    valid &= validateActiveDate("delegationMembers["+i+"].activeToDate", delegationMember.getActiveFromDate(), delegationMember.getActiveToDate());
559                    i++;
560                    }
561            return valid;
562        }
563    
564            protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
565                    // TODO : do not have detail bus rule yet, so just check this for now.
566                    boolean valid = true;
567                    if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
568                    GlobalVariables.getMessageMap().putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
569                valid = false;
570                    }
571                    return valid;
572            }
573    
574        public boolean processAddGroup(AddGroupEvent addGroupEvent) {
575            return getAddGroupRule().processAddGroup(addGroupEvent);
576        }
577    
578        public boolean processAddRole(AddRoleEvent addRoleEvent) {
579            return getAddRoleRule().processAddRole(addRoleEvent);
580        }
581    
582        public boolean processAddPersonDelegationMember(AddPersonDelegationMemberEvent addPersonDelegationMemberEvent){
583            return getAddPersonDelegationMemberRule().processAddPersonDelegationMember(addPersonDelegationMemberEvent);
584        }
585    
586            public IdentityService getIdentityService() {
587                    if ( identityService == null ) {
588                            identityService = KimApiServiceLocator.getIdentityService();
589                    }
590                    return identityService;
591            }
592    
593            public RoleService getRoleService() {
594                    if ( roleService == null ) {
595                            roleService = KimApiServiceLocator.getRoleService();
596                    }
597                    return roleService;
598            }
599    
600            public UiDocumentService getUIDocumentService() {
601                    if ( uiDocumentService == null ) {
602                            uiDocumentService = KIMServiceLocatorInternal.getUiDocumentService();
603                    }
604                    return uiDocumentService;
605            }
606    
607            public IdentityManagementKimDocumentAuthorizer getAuthorizer(IdentityManagementPersonDocument document) {
608                    if ( authorizer == null ) {
609                            authorizer = (IdentityManagementKimDocumentAuthorizer) KRADServiceLocatorWeb.getDocumentDictionaryService().getDocumentAuthorizer(document);
610                    }
611                    return authorizer;
612            }
613    
614    
615    
616            /**
617             * @return the addGroupRuleClass
618             */
619            public Class<? extends AddGroupRule> getAddGroupRuleClass() {
620                    return this.addGroupRuleClass;
621            }
622    
623    
624    
625            /**
626             * Can be overridden by subclasses to indicate the rule class to use when adding groups.
627             *
628             * @param addGroupRuleClass the addGroupRuleClass to set
629             */
630            public void setAddGroupRuleClass(Class<? extends AddGroupRule> addGroupRuleClass) {
631                    this.addGroupRuleClass = addGroupRuleClass;
632            }
633    
634    
635    
636            /**
637             * @return the addRoleRuleClass
638             */
639            public Class<? extends AddRoleRule> getAddRoleRuleClass() {
640                    return this.addRoleRuleClass;
641            }
642    
643    
644    
645            /**
646             * Can be overridden by subclasses to indicate the rule class to use when adding roles.
647             *
648             * @param addRoleRuleClass the addRoleRuleClass to set
649             */
650            public void setAddRoleRuleClass(Class<? extends AddRoleRule> addRoleRuleClass) {
651                    this.addRoleRuleClass = addRoleRuleClass;
652            }
653    
654    
655    
656            /**
657             * @return the addGroupRule
658             */
659            public AddGroupRule getAddGroupRule() {
660                    if ( addGroupRule == null ) {
661                            try {
662                                    addGroupRule = addGroupRuleClass.newInstance();
663                            } catch ( Exception ex ) {
664                                    throw new RuntimeException( "Unable to create AddGroupRule instance using class: " + addGroupRuleClass, ex );
665                            }
666                    }
667                    return addGroupRule;
668            }
669    
670    
671    
672            /**
673             * @return the addRoleRule
674             */
675            public AddRoleRule getAddRoleRule() {
676                    if ( addRoleRule == null ) {
677                            try {
678                                    addRoleRule = addRoleRuleClass.newInstance();
679                            } catch ( Exception ex ) {
680                                    throw new RuntimeException( "Unable to create AddRoleRule instance using class: " + addRoleRuleClass, ex );
681                            }
682                    }
683                    return addRoleRule;
684            }
685    
686            /**
687             * @return the addRoleRule
688             */
689            public AddPersonDelegationMemberRule getAddPersonDelegationMemberRule() {
690                    if(addPersonDelegationMemberRule == null){
691                            try {
692                                    addPersonDelegationMemberRule = addPersonDelegationMemberRuleClass.newInstance();
693                            } catch ( Exception ex ) {
694                                    throw new RuntimeException( "Unable to create AddPersonDelegationMemberRuleClass instance using class: " + addPersonDelegationMemberRuleClass, ex );
695                            }
696                    }
697                    return addPersonDelegationMemberRule;
698            }
699    
700            /**
701             * @return the businessObjectService
702             */
703            public BusinessObjectService getBusinessObjectService() {
704                    if ( businessObjectService == null ) {
705                            businessObjectService = KRADServiceLocator.getBusinessObjectService();
706                    }
707                    return businessObjectService;
708            }
709    
710            public boolean processAddPersonDocumentRoleQualifier(IdentityManagementPersonDocument document, PersonDocumentRole role, KimDocumentRoleMember kimDocumentRoleMember, int selectedRoleIdx) {
711                    boolean dateValidationSuccess = validateActiveDate("document.roles[" + selectedRoleIdx + "].newRolePrncpl.activeFromDate", kimDocumentRoleMember.getActiveFromDate(), kimDocumentRoleMember.getActiveToDate());
712                    String errorPath = "roles[" + selectedRoleIdx + "].newRolePrncpl";
713                    List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
714            GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
715            KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(KimTypeBo.to(role.getKimRoleType()));
716    
717                    List<RemotableAttributeError> errorsAttributesAgainstExisting;
718                int i = 0;
719                boolean rulePassed = true;
720                Map<String, String> newMemberQualifiers = attributeValidationHelper.convertQualifiersToMap(kimDocumentRoleMember.getQualifiers());
721                Map<String, String> oldMemberQualifiers;
722                List<String> roleIds = new ArrayList<String>();
723                roleIds.add(role.getRoleId());
724                for(KimDocumentRoleMember member: role.getRolePrncpls()){
725                    oldMemberQualifiers = member.getQualifierAsMap();
726                    errorsAttributesAgainstExisting = kimTypeService.validateUniqueAttributes(
727                                    role.getKimRoleType().getId(), newMemberQualifiers, oldMemberQualifiers);
728                    validationErrors.addAll(
729                                            attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
730                                .convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
731                                errorsAttributesAgainstExisting));
732                    i++;
733                }
734    
735            if ( kimTypeService != null ) {
736                    List<RemotableAttributeError> localErrors = kimTypeService.validateAttributes( role.getKimRoleType().getId(), newMemberQualifiers );
737                validationErrors.addAll( attributeValidationHelper.convertErrors(errorPath,
738                        attributeValidationHelper.convertQualifiersToAttrIdxMap(kimDocumentRoleMember.getQualifiers()),
739                        localErrors));
740            }
741    
742            GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
743            if (validationErrors.isEmpty()) {
744                    rulePassed = dateValidationSuccess;
745            } else {
746                    attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
747                    rulePassed = false;
748            }
749            return rulePassed;
750            }
751    
752        protected boolean validateDelegationMemberRoleQualifier(List<RoleDocumentDelegationMember> delegationMembers){
753                    List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
754                    boolean valid;
755                    int memberCounter = 0;
756                    List<RemotableAttributeError> errorsTemp;
757                    Map<String, String> mapToValidate;
758            KimTypeService kimTypeService;
759            GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
760            RoleMemberBo roleMember;
761            String errorPath;
762            ArrayList<String> roleIds;
763            KimType kimType;
764                    for(RoleDocumentDelegationMember delegationMember: delegationMembers) {
765                            kimType = KimTypeBo.to(delegationMember.getRoleBo().getKimRoleType());
766                            kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
767                            roleIds = new ArrayList<String>();
768                            roleIds.add(delegationMember.getRoleBo().getId());
769                            errorPath = "delegationMembers["+memberCounter+"]";
770                            mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers());
771                            errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
772                            validationErrors.addAll(
773                                            attributeValidationHelper.convertErrors(errorPath,
774                                attributeValidationHelper.convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()),
775                                errorsTemp));
776    
777                            roleMember = getUIDocumentService().getRoleMember(delegationMember.getRoleMemberId());
778                            if(roleMember==null){
779                                    valid = false;
780                                    GlobalVariables.getMessageMap().putError("document."+errorPath, RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{});
781                            } else{
782                                    kimTypeService.validateUnmodifiableAttributes(kimType.getId(), roleMember.getAttributes(), mapToValidate);
783                                    validationErrors.addAll(
784                                                    attributeValidationHelper.convertErrors(errorPath, attributeValidationHelper
785                                    .convertQualifiersToAttrIdxMap(delegationMember.getQualifiers()), errorsTemp));
786                            }
787                    memberCounter++;
788            }
789                    GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
790            if (validationErrors.isEmpty()) {
791                    valid = true;
792            } else {
793                    attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
794                    valid = false;
795            }
796            return valid;
797        }
798    
799    }