View Javadoc
1   /*
2    * Copyright 2009 The Kuali Foundation.
3    * 
4    * Licensed under the Educational Community License, Version 1.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/ecl1.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.ole.sec.document.validation.impl;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.ole.sec.SecConstants;
23  import org.kuali.ole.sec.SecKeyConstants;
24  import org.kuali.ole.sec.SecPropertyConstants;
25  import org.kuali.ole.sec.businessobject.SecurityDefinition;
26  import org.kuali.ole.sec.businessobject.SecurityModel;
27  import org.kuali.ole.sec.businessobject.SecurityModelDefinition;
28  import org.kuali.ole.sec.businessobject.SecurityModelMember;
29  import org.kuali.ole.sys.OLEPropertyConstants;
30  import org.kuali.ole.sys.context.SpringContext;
31  import org.kuali.rice.core.api.membership.MemberType;
32  import org.kuali.rice.kim.api.group.Group;
33  import org.kuali.rice.kim.api.group.GroupService;
34  import org.kuali.rice.kim.api.identity.principal.Principal;
35  import org.kuali.rice.kim.api.role.Role;
36  import org.kuali.rice.kim.api.role.RoleService;
37  import org.kuali.rice.kim.api.services.IdentityManagementService;
38  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
39  import org.kuali.rice.kns.document.MaintenanceDocument;
40  import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
41  import org.kuali.rice.krad.bo.PersistableBusinessObject;
42  import org.kuali.rice.krad.service.BusinessObjectService;
43  import org.kuali.rice.krad.util.GlobalVariables;
44  import org.kuali.rice.krad.util.KRADConstants;
45  import org.kuali.rice.krad.util.ObjectUtils;
46  
47  
48  /**
49   * Implements business rules checks on the SecurityModel maintenance document
50   */
51  public class SecurityModelRule extends MaintenanceDocumentRuleBase {
52      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelRule.class);
53  
54      private SecurityModel oldSecurityModel;
55      private SecurityModel newSecurityModel;
56  
57      protected volatile static BusinessObjectService businessObjectService;
58  
59      public SecurityModelRule() {
60          super();
61      }
62  
63      /**
64       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
65       */
66      @Override
67      protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
68          boolean isValid = super.processCustomApproveDocumentBusinessRules(document);
69  
70          if (!isValid) {
71              return isValid;
72          }
73  
74          boolean isMaintenanceEdit = document.isEdit();
75  
76          isValid &= validateSecurityModel(isMaintenanceEdit);
77  
78          return isValid;
79      }
80  
81      /**
82       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
83       */
84      @Override
85      protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
86          boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
87  
88          if (!isValid) {
89              return isValid;
90          }
91  
92          boolean isMaintenanceEdit = document.isEdit();
93  
94          isValid &= validateSecurityModel(isMaintenanceEdit);
95  
96          return isValid;
97      }
98  
99      /**
100      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
101      *      java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
102      */
103     @Override
104     public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
105         boolean isValid = super.processCustomAddCollectionLineBusinessRules(document, collectionName, line);
106 
107         if (!isValid) {
108             return isValid;
109         }
110 
111         if (SecPropertyConstants.MODEL_DEFINITIONS.equals(collectionName)) {
112             isValid &= validateModelDefinition((SecurityModelDefinition) line, "");
113         }
114 
115         if (SecPropertyConstants.MODEL_MEMBERS.equals(collectionName)) {
116             isValid &= validateModelMember((SecurityModelMember) line, "");
117         }
118 
119         return isValid;
120     }
121 
122     /**
123      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
124      */
125     @Override
126     public void setupConvenienceObjects() {
127         oldSecurityModel = (SecurityModel) super.getOldBo();
128         newSecurityModel = (SecurityModel) super.getNewBo();
129     }
130 
131     /**
132      * Validates the new security model record
133      * 
134      * @param isMaintenanceEdit boolean indicating whether the maintenance action is an edit (true), or a new/copy (false)
135      * @return boolean true if validation was successful, false if there are errors
136      */
137     protected boolean validateSecurityModel(boolean isMaintenanceEdit) {
138         boolean isValid = true;
139 
140         if (!isMaintenanceEdit) {
141             boolean validModelName = verifyModelNameIsUnique(newSecurityModel, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE);
142             if (!validModelName) {
143                 isValid = false;
144             }
145         }
146 
147         // check to make sure there is at least one model definition
148         if (newSecurityModel.getModelDefinitions() == null || newSecurityModel.getModelDefinitions().size() == 0) {
149             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, SecKeyConstants.ERROR_MODEL_DEFINITION_MISSING);
150         }
151 
152         int index = 0;
153         for (SecurityModelDefinition modelDefinition : newSecurityModel.getModelDefinitions()) {
154             String errorKeyPrefix = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_DEFINITIONS + "[" + index + "].";
155 
156             boolean modelDefinitionValid = validateModelDefinition(modelDefinition, errorKeyPrefix);
157             if (!modelDefinitionValid) {
158                 isValid = false;
159             }
160 
161             index++;
162         }
163 
164         index = 0;
165         for (SecurityModelMember modelMember : newSecurityModel.getModelMembers()) {
166             String errorKeyPrefix = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_MEMBERS + "[" + index + "].";
167 
168             boolean modelMemberValid = validateModelMember(modelMember, errorKeyPrefix);
169             if (!modelMemberValid) {
170                 isValid = false;
171             }
172 
173             index++;
174         }
175 
176 
177         return isValid;
178     }
179 
180     /**
181      * For new or copy action verifies the name given for the model is not being used by another model or definition
182      * 
183      * @param securityModel SecurityModel with name to check
184      * @param errorKeyPrefix String errorPrefix to use if any errors are found
185      * @return boolean true if name exists, false if not
186      */
187     protected boolean verifyModelNameIsUnique(SecurityModel securityModel, String errorKeyPrefix) {
188         boolean isValid = true;
189 
190         Map<String, String> searchValues = new HashMap<String, String>();
191         searchValues.put(OLEPropertyConstants.NAME, securityModel.getName());
192 
193         int matchCount = getBusinessObjectService().countMatching(SecurityModel.class, searchValues);
194         if (matchCount > 0) {
195             GlobalVariables.getMessageMap().putError(errorKeyPrefix + OLEPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName());
196             isValid = false;
197         }
198         
199         matchCount = getBusinessObjectService().countMatching(SecurityDefinition.class, searchValues);
200         if (matchCount > 0) {
201             GlobalVariables.getMessageMap().putError(errorKeyPrefix + OLEPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName());
202             isValid = false;
203         }
204 
205         return isValid;
206     }
207 
208     /**
209      * Validates a definition assignment to the model
210      * 
211      * @param modelDefinition SecurityModelDefinition to validate
212      * @param errorKeyPrefix String errorPrefix to use if any errors are found
213      * @return boolean true if validation was successful, false if there are errors
214      */
215     protected boolean validateModelDefinition(SecurityModelDefinition modelDefinition, String errorKeyPrefix) {
216         boolean isValid = true;
217 
218         modelDefinition.refreshNonUpdateableReferences();
219         
220         if (ObjectUtils.isNull(modelDefinition.getSecurityDefinition())) {
221             return false;
222         }
223         
224         String attributeName = modelDefinition.getSecurityDefinition().getSecurityAttribute().getName();
225         String attributeValue = modelDefinition.getAttributeValue();
226 
227         // if value is blank (which is allowed) no need to validate
228         if (StringUtils.isBlank(attributeValue)) {
229             return true;
230         }
231         
232         // descend attributes do not allow multiple values or wildcards, and operator must be equal
233         if (SecConstants.SecurityAttributeNames.CHART_DESCEND_HIERARCHY.equals(attributeName) || SecConstants.SecurityAttributeNames.ORGANIZATION_DESCEND_HIERARCHY.equals(attributeName)) {
234             if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.MULTI_VALUE_SEPERATION_CHARACTER)) {
235                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_MULTI_ATTR_VALUE, attributeName);
236                 isValid = false;
237             }
238 
239             if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.WILDCARD_CHARACTER)) {
240                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_WILDCARD_ATTR_VALUE, attributeName);
241                 isValid = false;
242             }
243 
244             if (!SecConstants.SecurityDefinitionOperatorCodes.EQUAL.equals(modelDefinition.getOperatorCode())) {
245                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.OPERATOR_CODE, SecKeyConstants.ERROR_MODEL_DEFINITION_OPERATOR_CODE_NOT_EQUAL, attributeName);
246                 isValid = false;
247             }
248         }
249 
250         // validate attribute value for existence
251         isValid = isValid && SecurityValidationUtil.validateAttributeValue(attributeName, attributeValue, errorKeyPrefix);
252 
253         return isValid;
254     }
255 
256     /**
257      * Validates a member assignment to the model
258      * 
259      * @param modelMember SecurityModelMember to validate
260      * @param errorKeyPrefix String errorPrefix to use if any errors are found
261      * @return boolean true if validation was successful, false if there are errors
262      */
263     protected boolean validateModelMember(SecurityModelMember modelMember, String errorKeyPrefix) {
264         boolean isValid = true;
265 
266         String memberId = modelMember.getMemberId();
267         String memberTypeCode = modelMember.getMemberTypeCode();
268         
269         if (StringUtils.isBlank(memberId) || StringUtils.isBlank(memberTypeCode)) {
270             return false;
271         }
272 
273         if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) {
274             Principal principalInfo = KimApiServiceLocator.getIdentityService().getPrincipal(memberId);
275             if (principalInfo == null) {
276                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
277                 isValid = false;
278             }
279         }
280         else if (MemberType.ROLE.getCode().equals(memberTypeCode)) {
281             Role roleInfo = KimApiServiceLocator.getRoleService().getRole(memberId);
282             if (roleInfo == null) {
283                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
284                 isValid = false;
285             }
286         }
287         else if (MemberType.GROUP.getCode().equals(memberTypeCode)) {
288             Group groupInfo = KimApiServiceLocator.getGroupService().getGroup(memberId);
289             if (groupInfo == null) {
290                 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
291                 isValid = false;
292             }
293         }
294 
295         return isValid;
296     }
297 
298     /**
299      * @return the default implementation of the business object service
300      */
301     protected BusinessObjectService getBusinessObjectService() {
302         if (businessObjectService == null) {
303             businessObjectService = SpringContext.getBean(BusinessObjectService.class);
304         }
305         return businessObjectService;
306     }
307 }