View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.document.rule;
17  
18  import java.sql.Timestamp;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.collections.CollectionUtils;
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.rice.core.api.uif.RemotableAttributeError;
27  import org.kuali.rice.core.api.util.RiceKeyConstants;
28  import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
29  import org.kuali.rice.coreservice.api.namespace.Namespace;
30  import org.kuali.rice.kim.api.KimConstants;
31  import org.kuali.rice.kim.api.group.Group;
32  import org.kuali.rice.kim.api.identity.IdentityService;
33  import org.kuali.rice.kim.api.identity.principal.Principal;
34  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
35  import org.kuali.rice.kim.api.type.KimType;
36  import org.kuali.rice.kim.bo.ui.GroupDocumentMember;
37  import org.kuali.rice.kim.bo.ui.GroupDocumentQualifier;
38  import org.kuali.rice.kim.document.IdentityManagementGroupDocument;
39  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
40  import org.kuali.rice.kim.framework.type.KimTypeService;
41  import org.kuali.rice.kim.rule.event.ui.AddGroupMemberEvent;
42  import org.kuali.rice.kim.rule.ui.AddGroupMemberRule;
43  import org.kuali.rice.kim.rules.ui.GroupDocumentMemberRule;
44  import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
45  import org.kuali.rice.krad.document.Document;
46  import org.kuali.rice.krad.util.GlobalVariables;
47  import org.kuali.rice.krad.util.KRADConstants;
48  import org.kuali.rice.krad.util.MessageMap;
49  
50  /**
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public class IdentityManagementGroupDocumentRule extends TransactionalDocumentRuleBase implements AddGroupMemberRule {
54  
55  	protected AddGroupMemberRule addGroupMemberRule;
56  	protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper();
57  	
58  	protected Class<? extends GroupDocumentMemberRule> addGroupMemberRuleClass = GroupDocumentMemberRule.class;
59  
60  	protected IdentityService identityService; 
61  	
62      public IdentityService getIdentityService() {
63          if ( identityService == null) {
64              identityService = KimApiServiceLocator.getIdentityService();
65          }
66          return identityService;
67      }
68  
69      @Override
70      protected boolean processCustomSaveDocumentBusinessRules(Document document) {
71          if (!(document instanceof IdentityManagementGroupDocument)) {
72              return false;
73          }
74  
75          IdentityManagementGroupDocument groupDoc = (IdentityManagementGroupDocument)document;
76  
77          boolean valid = true;
78          GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
79          valid &= validAssignGroup(groupDoc);
80          valid &= validDuplicateGroupName(groupDoc);
81          getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false);
82          valid &= validateGroupQualifier(groupDoc.getQualifiers(), groupDoc.getKimType());
83          valid &= validGroupMemberActiveDates(groupDoc.getMembers());
84          //KULRICE-6858 Validate group members are in identity system
85          valid &= validGroupMemberPrincipalIDs(groupDoc.getMembers());
86          GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
87  
88          return valid;
89      }
90  
91      //Added method to validate existence of a group namespace on a submitted IdentityManagementGroupDocument
92      protected boolean validGroupNamespace(IdentityManagementGroupDocument document){
93          boolean rulePassed = true;
94          String namespacePassed = document.getGroupNamespace();
95          if(StringUtils.isNotBlank(namespacePassed)){
96              rulePassed = true;
97              Namespace matchedNamespace = CoreServiceApiServiceLocator.getNamespaceService().getNamespace(namespacePassed);
98              if(matchedNamespace == null){
99                  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 }