001/** 002 * Copyright 2005-2017 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kim.document.rule; 017 018import java.sql.Timestamp; 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.collections.CollectionUtils; 025import org.apache.commons.lang.StringUtils; 026import org.kuali.rice.core.api.uif.RemotableAttributeError; 027import org.kuali.rice.core.api.util.RiceKeyConstants; 028import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator; 029import org.kuali.rice.coreservice.api.namespace.Namespace; 030import org.kuali.rice.kim.api.KimConstants; 031import org.kuali.rice.kim.api.group.Group; 032import org.kuali.rice.kim.api.identity.IdentityService; 033import org.kuali.rice.kim.api.identity.principal.Principal; 034import org.kuali.rice.kim.api.services.KimApiServiceLocator; 035import org.kuali.rice.kim.api.type.KimType; 036import org.kuali.rice.kim.bo.ui.GroupDocumentMember; 037import org.kuali.rice.kim.bo.ui.GroupDocumentQualifier; 038import org.kuali.rice.kim.document.IdentityManagementGroupDocument; 039import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator; 040import org.kuali.rice.kim.framework.type.KimTypeService; 041import org.kuali.rice.kim.rule.event.ui.AddGroupMemberEvent; 042import org.kuali.rice.kim.rule.ui.AddGroupMemberRule; 043import org.kuali.rice.kim.rules.ui.GroupDocumentMemberRule; 044import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase; 045import org.kuali.rice.krad.document.Document; 046import org.kuali.rice.krad.util.GlobalVariables; 047import org.kuali.rice.krad.util.KRADConstants; 048import org.kuali.rice.krad.util.MessageMap; 049 050/** 051 * @author Kuali Rice Team (rice.collab@kuali.org) 052 */ 053public class IdentityManagementGroupDocumentRule extends TransactionalDocumentRuleBase implements AddGroupMemberRule { 054 055 protected AddGroupMemberRule addGroupMemberRule; 056 protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper(); 057 058 protected Class<? extends GroupDocumentMemberRule> addGroupMemberRuleClass = GroupDocumentMemberRule.class; 059 060 protected IdentityService identityService; 061 062 public IdentityService getIdentityService() { 063 if ( identityService == null) { 064 identityService = KimApiServiceLocator.getIdentityService(); 065 } 066 return identityService; 067 } 068 069 @Override 070 protected boolean processCustomSaveDocumentBusinessRules(Document document) { 071 if (!(document instanceof IdentityManagementGroupDocument)) { 072 return false; 073 } 074 075 IdentityManagementGroupDocument groupDoc = (IdentityManagementGroupDocument)document; 076 077 boolean valid = true; 078 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 079 valid &= validAssignGroup(groupDoc); 080 valid &= validDuplicateGroupName(groupDoc); 081 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false); 082 valid &= validateGroupQualifier(groupDoc.getQualifiers(), groupDoc.getKimType()); 083 valid &= validGroupMemberActiveDates(groupDoc.getMembers()); 084 //KULRICE-6858 Validate group members are in identity system 085 valid &= validGroupMemberPrincipalIDs(groupDoc.getMembers()); 086 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 087 088 return valid; 089 } 090 091 //Added method to validate existence of a group namespace on a submitted IdentityManagementGroupDocument 092 protected boolean validGroupNamespace(IdentityManagementGroupDocument document){ 093 boolean rulePassed = true; 094 String namespacePassed = document.getGroupNamespace(); 095 if(StringUtils.isNotBlank(namespacePassed)){ 096 rulePassed = true; 097 Namespace matchedNamespace = CoreServiceApiServiceLocator.getNamespaceService().getNamespace(namespacePassed); 098 if(matchedNamespace == null){ 099 rulePassed = false; 100 } 101 }else{ 102 rulePassed = false; 103 } 104 if(!rulePassed){ 105 GlobalVariables.getMessageMap().putError("document.groupNamespace", 106 RiceKeyConstants.ERROR_REQUIRED, 107 new String[] {"Group Namespace"}); 108 } 109 return rulePassed; 110 } 111 112 protected boolean validAssignGroup(IdentityManagementGroupDocument document){ 113 boolean rulePassed = true; 114 Map<String,String> additionalPermissionDetails = new HashMap<String,String>(); 115 additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, document.getGroupNamespace()); 116 additionalPermissionDetails.put(KimConstants.AttributeConstants.GROUP_NAME, document.getGroupName()); 117 if(document.getMembers()!=null && document.getMembers().size()>0){ 118 if(!getDocumentDictionaryService().getDocumentAuthorizer(document).isAuthorizedByTemplate( 119 document, KimConstants.NAMESPACE_CODE, KimConstants.PermissionTemplateNames.POPULATE_GROUP, 120 GlobalVariables.getUserSession().getPrincipalId(), additionalPermissionDetails, null)){ 121 GlobalVariables.getMessageMap().putError("document.groupName", 122 RiceKeyConstants.ERROR_ASSIGN_GROUP, 123 new String[] {document.getGroupNamespace(), document.getGroupName()}); 124 rulePassed = false; 125 } 126 } 127 return rulePassed; 128 } 129 130 protected boolean validDuplicateGroupName(IdentityManagementGroupDocument groupDoc){ 131 Group group = null; 132 if(null != groupDoc.getGroupNamespace() && null != groupDoc.getGroupName()){ 133 group = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName( 134 groupDoc.getGroupNamespace(), groupDoc.getGroupName()); 135 } 136 boolean rulePassed = true; 137 if(group!=null){ 138 if(group.getId().equals(groupDoc.getGroupId())) { 139 rulePassed = true; 140 } 141 else{ 142 GlobalVariables.getMessageMap().putError("document.groupName", 143 RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Group Name"}); 144 rulePassed = false; 145 } 146 } 147 return rulePassed; 148 } 149 150 protected boolean validGroupMemberActiveDates(List<GroupDocumentMember> groupMembers) { 151 boolean valid = true; 152 int i = 0; 153 for(GroupDocumentMember groupMember: groupMembers) { 154 valid &= validateActiveDate("document.members["+i+"].activeToDate", groupMember.getActiveFromDate(), groupMember.getActiveToDate()); 155 i++; 156 } 157 return valid; 158 } 159 160 protected boolean validGroupMemberPrincipalIDs(List<GroupDocumentMember> groupMembers) { 161 boolean valid = true; 162 List<String> principalIds = new ArrayList<String>(); 163 for(GroupDocumentMember groupMember: groupMembers) { 164 if (StringUtils.equals(groupMember.getMemberTypeCode(), KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()) ) { 165 principalIds.add(groupMember.getMemberId()); 166 } 167 } 168 if(!principalIds.isEmpty()) { 169 // retrieve valid principals/principal-ids from identity service 170 List<Principal> validPrincipals = getIdentityService().getPrincipals(principalIds); 171 List<String> validPrincipalIds = new ArrayList<String>(validPrincipals.size()); 172 for (Principal principal : validPrincipals) { 173 validPrincipalIds.add(principal.getPrincipalId()); 174 } 175 // check that there are no invalid principals in the principal list, return false 176 List<String> invalidPrincipalIds = new ArrayList<String>(CollectionUtils.subtract(principalIds, validPrincipalIds)); 177 // if list is not empty add error messages and return false 178 if(CollectionUtils.isNotEmpty(invalidPrincipalIds)) { 179 GlobalVariables.getMessageMap().putError("document.member.memberId", RiceKeyConstants.ERROR_MEMBERID_MEMBERTYPE_MISMATCH, 180 invalidPrincipalIds.toArray(new String[invalidPrincipalIds.size()])); 181 valid = false; 182 } 183 } 184 return valid; 185 } 186 187 protected boolean validateGroupQualifier(List<GroupDocumentQualifier> groupQualifiers, KimType kimType){ 188 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 189 190 List<RemotableAttributeError> errorsTemp; 191 Map<String, String> mapToValidate; 192 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType); 193 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 194 mapToValidate = attributeValidationHelper.convertQualifiersToMap(groupQualifiers); 195 errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate); 196 validationErrors.addAll(attributeValidationHelper.convertErrors("", 197 attributeValidationHelper.convertQualifiersToAttrIdxMap(groupQualifiers), errorsTemp)); 198 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 199 200 if (validationErrors.isEmpty()) { 201 return true; 202 } 203 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 204 return false; 205 } 206 207 protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) { 208 // TODO : do not have detail bus rule yet, so just check this for now. 209 boolean valid = true; 210 if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) { 211 MessageMap errorMap = GlobalVariables.getMessageMap(); 212 errorMap.putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE); 213 valid = false; 214 215 } 216 return valid; 217 } 218 219 /** 220 * @return the addGroupMemberRule 221 */ 222 public AddGroupMemberRule getAddGroupMemberRule() { 223 if(addGroupMemberRule == null){ 224 try { 225 addGroupMemberRule = addGroupMemberRuleClass.newInstance(); 226 } catch ( Exception ex ) { 227 throw new RuntimeException( "Unable to create AddMemberRule instance using class: " + addGroupMemberRuleClass, ex ); 228 } 229 } 230 return addGroupMemberRule; 231 } 232 233 public boolean processAddGroupMember(AddGroupMemberEvent addGroupMemberEvent) { 234 return getAddGroupMemberRule().processAddGroupMember(addGroupMemberEvent); 235 } 236 237}