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