View Javadoc
1   /**
2    * Copyright 2005-2014 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.kim.api.KimConstants;
29  import org.kuali.rice.kim.api.group.Group;
30  import org.kuali.rice.kim.api.identity.IdentityService;
31  import org.kuali.rice.kim.api.identity.principal.Principal;
32  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
33  import org.kuali.rice.kim.api.type.KimType;
34  import org.kuali.rice.kim.bo.ui.GroupDocumentMember;
35  import org.kuali.rice.kim.bo.ui.GroupDocumentQualifier;
36  import org.kuali.rice.kim.document.IdentityManagementGroupDocument;
37  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
38  import org.kuali.rice.kim.framework.type.KimTypeService;
39  import org.kuali.rice.kim.rule.event.ui.AddGroupMemberEvent;
40  import org.kuali.rice.kim.rule.ui.AddGroupMemberRule;
41  import org.kuali.rice.kim.rules.ui.GroupDocumentMemberRule;
42  import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
43  import org.kuali.rice.krad.document.Document;
44  import org.kuali.rice.krad.util.GlobalVariables;
45  import org.kuali.rice.krad.util.KRADConstants;
46  import org.kuali.rice.krad.util.MessageMap;
47  
48  /**
49   * @author Kuali Rice Team (rice.collab@kuali.org)
50   */
51  public class IdentityManagementGroupDocumentRule extends TransactionalDocumentRuleBase implements AddGroupMemberRule {
52  
53  	protected AddGroupMemberRule addGroupMemberRule;
54  	protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper();
55  	
56  	protected Class<? extends GroupDocumentMemberRule> addGroupMemberRuleClass = GroupDocumentMemberRule.class;
57  
58  	protected IdentityService identityService; 
59  	
60      public IdentityService getIdentityService() {
61          if ( identityService == null) {
62              identityService = KimApiServiceLocator.getIdentityService();
63          }
64          return identityService;
65      }
66  
67      @Override
68      protected boolean processCustomSaveDocumentBusinessRules(Document document) {
69          if (!(document instanceof IdentityManagementGroupDocument)) {
70              return false;
71          }
72  
73          IdentityManagementGroupDocument groupDoc = (IdentityManagementGroupDocument)document;
74  
75          boolean valid = true;
76          GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
77          valid &= validAssignGroup(groupDoc);
78          valid &= validDuplicateGroupName(groupDoc);
79          getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false);
80          valid &= validateGroupQualifier(groupDoc.getQualifiers(), groupDoc.getKimType());
81          valid &= validGroupMemberActiveDates(groupDoc.getMembers());
82          //KULRICE-6858 Validate group members are in identity system
83          valid &= validGroupMemberPrincipalIDs(groupDoc.getMembers());
84          GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
85  
86          return valid;
87      }
88      
89  	protected boolean validAssignGroup(IdentityManagementGroupDocument document){
90          boolean rulePassed = true;
91          Map<String,String> additionalPermissionDetails = new HashMap<String,String>();
92          additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, document.getGroupNamespace());
93          additionalPermissionDetails.put(KimConstants.AttributeConstants.GROUP_NAME, document.getGroupName());
94  		if(document.getMembers()!=null && document.getMembers().size()>0){
95  			if(!getDocumentDictionaryService().getDocumentAuthorizer(document).isAuthorizedByTemplate(
96  					document, KimConstants.NAMESPACE_CODE, KimConstants.PermissionTemplateNames.POPULATE_GROUP,
97  					GlobalVariables.getUserSession().getPrincipalId(), additionalPermissionDetails, null)){
98  	    		GlobalVariables.getMessageMap().putError("document.groupName", 
99  	    				RiceKeyConstants.ERROR_ASSIGN_GROUP, 
100 	    				new String[] {document.getGroupNamespace(), document.getGroupName()});
101 	            rulePassed = false;
102 			}
103 		}
104 		return rulePassed;
105 	}
106 
107 	protected boolean validDuplicateGroupName(IdentityManagementGroupDocument groupDoc){
108         Group group = null;
109         if(null != groupDoc.getGroupNamespace() && null != groupDoc.getGroupName()){
110             group = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(
111                 groupDoc.getGroupNamespace(), groupDoc.getGroupName());
112         }
113         boolean rulePassed = true;
114     	if(group!=null){
115     		if(group.getId().equals(groupDoc.getGroupId())) {
116     			rulePassed = true;
117             }
118     		else{
119 	    		GlobalVariables.getMessageMap().putError("document.groupName", 
120 	    				RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Group Name"});
121 	    		rulePassed = false;
122     		}
123     	}
124     	return rulePassed;
125     }
126     
127     protected boolean validGroupMemberActiveDates(List<GroupDocumentMember> groupMembers) {
128     	boolean valid = true;
129 		int i = 0;
130     	for(GroupDocumentMember groupMember: groupMembers) {
131    			valid &= validateActiveDate("document.members["+i+"].activeToDate", groupMember.getActiveFromDate(), groupMember.getActiveToDate());
132     		i++;
133     	}
134     	return valid;
135     }
136 
137     protected boolean validGroupMemberPrincipalIDs(List<GroupDocumentMember> groupMembers) {
138         boolean valid = true;
139         List<String> principalIds = new ArrayList<String>();
140         for(GroupDocumentMember groupMember: groupMembers) {
141             if (StringUtils.equals(groupMember.getMemberTypeCode(), KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()) ) {
142                 principalIds.add(groupMember.getMemberId());
143             }
144         }
145         if(!principalIds.isEmpty())       {
146             // retrieve valid principals/principal-ids from identity service
147             List<Principal> validPrincipals = getIdentityService().getPrincipals(principalIds);
148             List<String> validPrincipalIds = new ArrayList<String>(validPrincipals.size());
149             for (Principal principal : validPrincipals) {
150                 validPrincipalIds.add(principal.getPrincipalId());
151             }
152             // check that there are no invalid principals in the principal list, return false
153             List<String> invalidPrincipalIds = new ArrayList<String>(CollectionUtils.subtract(principalIds, validPrincipalIds));
154             // if list is not empty add error messages and return false
155             if(CollectionUtils.isNotEmpty(invalidPrincipalIds)) {
156                 GlobalVariables.getMessageMap().putError("document.member.memberId", RiceKeyConstants.ERROR_MEMBERID_MEMBERTYPE_MISMATCH,
157                         invalidPrincipalIds.toArray(new String[invalidPrincipalIds.size()]));
158                 valid = false;
159             }
160         }
161         return valid;
162     }
163 
164     protected boolean validateGroupQualifier(List<GroupDocumentQualifier> groupQualifiers, KimType kimType){
165 		List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
166 
167 		List<RemotableAttributeError> errorsTemp;
168 		Map<String, String> mapToValidate;
169         KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
170         GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
171 		mapToValidate = attributeValidationHelper.convertQualifiersToMap(groupQualifiers);
172 		errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate);
173 		validationErrors.addAll(attributeValidationHelper.convertErrors("",
174                 attributeValidationHelper.convertQualifiersToAttrIdxMap(groupQualifiers), errorsTemp));
175 		GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
176 		
177     	if (validationErrors.isEmpty()) {
178     		return true;
179     	} 
180     	attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors);
181     	return false;
182     }
183     
184 	protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) {
185 		// TODO : do not have detail bus rule yet, so just check this for now.
186 		boolean valid = true;
187 		if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) {
188 	        MessageMap errorMap = GlobalVariables.getMessageMap();
189             errorMap.putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE);
190             valid = false;
191 			
192 		}
193 		return valid;
194 	}
195 	
196 	/**
197 	 * @return the addGroupMemberRule
198 	 */
199 	public AddGroupMemberRule getAddGroupMemberRule() {
200 		if(addGroupMemberRule == null){
201 			try {
202 				addGroupMemberRule = addGroupMemberRuleClass.newInstance();
203 			} catch ( Exception ex ) {
204 				throw new RuntimeException( "Unable to create AddMemberRule instance using class: " + addGroupMemberRuleClass, ex );
205 			}
206 		}
207 		return addGroupMemberRule;
208 	}
209 
210     public boolean processAddGroupMember(AddGroupMemberEvent addGroupMemberEvent) {
211         return getAddGroupMemberRule().processAddGroupMember(addGroupMemberEvent);    
212     }
213 
214 }