001/* 002 * Copyright 2009 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 1.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/ecl1.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 */ 016package org.kuali.ole.sec.document.validation.impl; 017 018import java.util.HashMap; 019import java.util.Map; 020 021import org.apache.commons.lang.StringUtils; 022import org.kuali.ole.sec.SecConstants; 023import org.kuali.ole.sec.SecKeyConstants; 024import org.kuali.ole.sec.SecPropertyConstants; 025import org.kuali.ole.sec.businessobject.SecurityDefinition; 026import org.kuali.ole.sec.businessobject.SecurityModel; 027import org.kuali.ole.sec.businessobject.SecurityModelDefinition; 028import org.kuali.ole.sec.businessobject.SecurityModelMember; 029import org.kuali.ole.sys.OLEPropertyConstants; 030import org.kuali.ole.sys.context.SpringContext; 031import org.kuali.rice.core.api.membership.MemberType; 032import org.kuali.rice.kim.api.group.Group; 033import org.kuali.rice.kim.api.group.GroupService; 034import org.kuali.rice.kim.api.identity.principal.Principal; 035import org.kuali.rice.kim.api.role.Role; 036import org.kuali.rice.kim.api.role.RoleService; 037import org.kuali.rice.kim.api.services.IdentityManagementService; 038import org.kuali.rice.kim.api.services.KimApiServiceLocator; 039import org.kuali.rice.kns.document.MaintenanceDocument; 040import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 041import org.kuali.rice.krad.bo.PersistableBusinessObject; 042import org.kuali.rice.krad.service.BusinessObjectService; 043import org.kuali.rice.krad.util.GlobalVariables; 044import org.kuali.rice.krad.util.KRADConstants; 045import org.kuali.rice.krad.util.ObjectUtils; 046 047 048/** 049 * Implements business rules checks on the SecurityModel maintenance document 050 */ 051public class SecurityModelRule extends MaintenanceDocumentRuleBase { 052 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelRule.class); 053 054 private SecurityModel oldSecurityModel; 055 private SecurityModel newSecurityModel; 056 057 protected volatile static BusinessObjectService businessObjectService; 058 059 public SecurityModelRule() { 060 super(); 061 } 062 063 /** 064 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 065 */ 066 @Override 067 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) { 068 boolean isValid = super.processCustomApproveDocumentBusinessRules(document); 069 070 if (!isValid) { 071 return isValid; 072 } 073 074 boolean isMaintenanceEdit = document.isEdit(); 075 076 isValid &= validateSecurityModel(isMaintenanceEdit); 077 078 return isValid; 079 } 080 081 /** 082 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 083 */ 084 @Override 085 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { 086 boolean isValid = super.processCustomRouteDocumentBusinessRules(document); 087 088 if (!isValid) { 089 return isValid; 090 } 091 092 boolean isMaintenanceEdit = document.isEdit(); 093 094 isValid &= validateSecurityModel(isMaintenanceEdit); 095 096 return isValid; 097 } 098 099 /** 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}