001 /*
002 * Copyright 2011 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 */
016 package org.kuali.kfs.sec.document.validation.impl;
017
018 import java.util.HashMap;
019 import java.util.Map;
020
021 import org.apache.commons.lang.StringUtils;
022 import org.kuali.kfs.sec.SecConstants;
023 import org.kuali.kfs.sec.SecKeyConstants;
024 import org.kuali.kfs.sec.SecPropertyConstants;
025 import org.kuali.kfs.sec.businessobject.SecurityDefinition;
026 import org.kuali.kfs.sec.businessobject.SecurityModel;
027 import org.kuali.kfs.sec.businessobject.SecurityModelDefinition;
028 import org.kuali.kfs.sec.businessobject.SecurityModelMember;
029 import org.kuali.kfs.sys.KFSPropertyConstants;
030 import org.kuali.kfs.sys.context.SpringContext;
031 import org.kuali.rice.kim.bo.entity.dto.KimPrincipalInfo;
032 import org.kuali.rice.kim.bo.group.dto.GroupInfo;
033 import org.kuali.rice.kim.bo.role.dto.KimRoleInfo;
034 import org.kuali.rice.kim.service.GroupService;
035 import org.kuali.rice.kim.service.IdentityManagementService;
036 import org.kuali.rice.kim.service.RoleManagementService;
037 import org.kuali.rice.kim.util.KimConstants;
038 import org.kuali.rice.kns.bo.PersistableBusinessObject;
039 import org.kuali.rice.kns.document.MaintenanceDocument;
040 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
041 import org.kuali.rice.kns.service.BusinessObjectService;
042 import org.kuali.rice.kns.util.GlobalVariables;
043 import org.kuali.rice.kns.util.KNSConstants;
044 import org.kuali.rice.kns.util.ObjectUtils;
045
046
047 /**
048 * Implements business rules checks on the SecurityModel maintenance document
049 */
050 public class SecurityModelRule extends MaintenanceDocumentRuleBase {
051 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelRule.class);
052
053 private SecurityModel oldSecurityModel;
054 private SecurityModel newSecurityModel;
055
056 public SecurityModelRule() {
057 super();
058 }
059
060 /**
061 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
062 */
063 @Override
064 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
065 boolean isValid = super.processCustomApproveDocumentBusinessRules(document);
066
067 if (!isValid) {
068 return isValid;
069 }
070
071 boolean isMaintenanceEdit = document.isEdit();
072
073 isValid &= validateSecurityModel(isMaintenanceEdit);
074
075 return isValid;
076 }
077
078 /**
079 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
080 */
081 @Override
082 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
083 boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
084
085 if (!isValid) {
086 return isValid;
087 }
088
089 boolean isMaintenanceEdit = document.isEdit();
090
091 isValid &= validateSecurityModel(isMaintenanceEdit);
092
093 return isValid;
094 }
095
096 /**
097 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
098 * java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
099 */
100 @Override
101 public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
102 boolean isValid = super.processCustomAddCollectionLineBusinessRules(document, collectionName, line);
103
104 if (!isValid) {
105 return isValid;
106 }
107
108 if (SecPropertyConstants.MODEL_DEFINITIONS.equals(collectionName)) {
109 isValid &= validateModelDefinition((SecurityModelDefinition) line, "");
110 }
111
112 if (SecPropertyConstants.MODEL_MEMBERS.equals(collectionName)) {
113 isValid &= validateModelMember((SecurityModelMember) line, "");
114 }
115
116 return isValid;
117 }
118
119 /**
120 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
121 */
122 @Override
123 public void setupConvenienceObjects() {
124 oldSecurityModel = (SecurityModel) super.getOldBo();
125 newSecurityModel = (SecurityModel) super.getNewBo();
126 }
127
128 /**
129 * Validates the new security model record
130 *
131 * @param isMaintenanceEdit boolean indicating whether the maintenance action is an edit (true), or a new/copy (false)
132 * @return boolean true if validation was successful, false if there are errors
133 */
134 protected boolean validateSecurityModel(boolean isMaintenanceEdit) {
135 boolean isValid = true;
136
137 if (!isMaintenanceEdit) {
138 boolean validModelName = verifyModelNameIsUnique(newSecurityModel, KNSConstants.MAINTENANCE_NEW_MAINTAINABLE);
139 if (!validModelName) {
140 isValid = false;
141 }
142 }
143
144 // check to make sure there is at least one model definition
145 if (newSecurityModel.getModelDefinitions() == null || newSecurityModel.getModelDefinitions().size() == 0) {
146 GlobalVariables.getMessageMap().putError(KNSConstants.GLOBAL_ERRORS, SecKeyConstants.ERROR_MODEL_DEFINITION_MISSING);
147 }
148
149 int index = 0;
150 for (SecurityModelDefinition modelDefinition : newSecurityModel.getModelDefinitions()) {
151 String errorKeyPrefix = KNSConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_DEFINITIONS + "[" + index + "].";
152
153 boolean modelDefinitionValid = validateModelDefinition(modelDefinition, errorKeyPrefix);
154 if (!modelDefinitionValid) {
155 isValid = false;
156 }
157
158 index++;
159 }
160
161 index = 0;
162 for (SecurityModelMember modelMember : newSecurityModel.getModelMembers()) {
163 String errorKeyPrefix = KNSConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_MEMBERS + "[" + index + "].";
164
165 boolean modelMemberValid = validateModelMember(modelMember, errorKeyPrefix);
166 if (!modelMemberValid) {
167 isValid = false;
168 }
169
170 index++;
171 }
172
173
174 return isValid;
175 }
176
177 /**
178 * For new or copy action verifies the name given for the model is not being used by another model or definition
179 *
180 * @param securityModel SecurityModel with name to check
181 * @param errorKeyPrefix String errorPrefix to use if any errors are found
182 * @return boolean true if name exists, false if not
183 */
184 protected boolean verifyModelNameIsUnique(SecurityModel securityModel, String errorKeyPrefix) {
185 boolean isValid = true;
186
187 Map<String, String> searchValues = new HashMap<String, String>();
188 searchValues.put(KFSPropertyConstants.NAME, securityModel.getName());
189
190 int matchCount = SpringContext.getBean(BusinessObjectService.class).countMatching(SecurityModel.class, searchValues);
191 if (matchCount > 0) {
192 GlobalVariables.getMessageMap().putError(errorKeyPrefix + KFSPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName());
193 isValid = false;
194 }
195
196 matchCount = SpringContext.getBean(BusinessObjectService.class).countMatching(SecurityDefinition.class, searchValues);
197 if (matchCount > 0) {
198 GlobalVariables.getMessageMap().putError(errorKeyPrefix + KFSPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName());
199 isValid = false;
200 }
201
202 return isValid;
203 }
204
205 /**
206 * Validates a definition assignment to the model
207 *
208 * @param modelDefinition SecurityModelDefinition to validate
209 * @param errorKeyPrefix String errorPrefix to use if any errors are found
210 * @return boolean true if validation was successful, false if there are errors
211 */
212 protected boolean validateModelDefinition(SecurityModelDefinition modelDefinition, String errorKeyPrefix) {
213 boolean isValid = true;
214
215 modelDefinition.refreshNonUpdateableReferences();
216
217 if (ObjectUtils.isNull(modelDefinition.getSecurityDefinition())) {
218 return false;
219 }
220
221 String attributeName = modelDefinition.getSecurityDefinition().getSecurityAttribute().getName();
222 String attributeValue = modelDefinition.getAttributeValue();
223
224 // if value is blank (which is allowed) no need to validate
225 if (StringUtils.isBlank(attributeValue)) {
226 return true;
227 }
228
229 // descend attributes do not allow multiple values or wildcards, and operator must be equal
230 if (SecConstants.SecurityAttributeNames.CHART_DESCEND_HIERARCHY.equals(attributeName) || SecConstants.SecurityAttributeNames.ORGANIZATION_DESCEND_HIERARCHY.equals(attributeName)) {
231 if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.MULTI_VALUE_SEPERATION_CHARACTER)) {
232 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_MULTI_ATTR_VALUE, attributeName);
233 isValid = false;
234 }
235
236 if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.WILDCARD_CHARACTER)) {
237 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_WILDCARD_ATTR_VALUE, attributeName);
238 isValid = false;
239 }
240
241 if (!SecConstants.SecurityDefinitionOperatorCodes.EQUAL.equals(modelDefinition.getOperatorCode())) {
242 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.OPERATOR_CODE, SecKeyConstants.ERROR_MODEL_DEFINITION_OPERATOR_CODE_NOT_EQUAL, attributeName);
243 isValid = false;
244 }
245 }
246
247 // validate attribute value for existence
248 isValid = isValid && SecurityValidationUtil.validateAttributeValue(attributeName, attributeValue, errorKeyPrefix);
249
250 return isValid;
251 }
252
253 /**
254 * Validates a member assignment to the model
255 *
256 * @param modelMember SecurityModelMember to validate
257 * @param errorKeyPrefix String errorPrefix to use if any errors are found
258 * @return boolean true if validation was successful, false if there are errors
259 */
260 protected boolean validateModelMember(SecurityModelMember modelMember, String errorKeyPrefix) {
261 boolean isValid = true;
262
263 String memberId = modelMember.getMemberId();
264 String memberTypeCode = modelMember.getMemberTypeCode();
265
266 if (StringUtils.isBlank(memberId) || StringUtils.isBlank(memberTypeCode)) {
267 return false;
268 }
269
270 if (KimConstants.KimUIConstants.MEMBER_TYPE_PRINCIPAL_CODE.equals(memberTypeCode)) {
271 KimPrincipalInfo principalInfo = SpringContext.getBean(IdentityManagementService.class).getPrincipal(memberId);
272 if (principalInfo == null) {
273 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
274 isValid = false;
275 }
276 }
277 else if (KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE.equals(memberTypeCode)) {
278 KimRoleInfo roleInfo = SpringContext.getBean(RoleManagementService.class).getRole(memberId);
279 if (roleInfo == null) {
280 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
281 isValid = false;
282 }
283 }
284 else if (KimConstants.KimUIConstants.MEMBER_TYPE_GROUP_CODE.equals(memberTypeCode)) {
285 GroupInfo groupInfo = SpringContext.getBean(GroupService.class).getGroupInfo(memberId);
286 if (groupInfo == null) {
287 GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode);
288 isValid = false;
289 }
290 }
291
292 return isValid;
293 }
294
295
296 }