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 }