/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.sec.document.validation.impl; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.sec.SecConstants; import org.kuali.kfs.sec.SecKeyConstants; import org.kuali.kfs.sec.SecPropertyConstants; import org.kuali.kfs.sec.businessobject.SecurityDefinition; import org.kuali.kfs.sec.businessobject.SecurityModel; import org.kuali.kfs.sec.businessobject.SecurityModelDefinition; import org.kuali.kfs.sec.businessobject.SecurityModelMember; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.rice.core.api.membership.MemberType; import org.kuali.rice.kim.api.group.Group; import org.kuali.rice.kim.api.group.GroupService; import org.kuali.rice.kim.api.identity.principal.Principal; import org.kuali.rice.kim.api.role.Role; import org.kuali.rice.kim.api.role.RoleService; import org.kuali.rice.kim.api.services.IdentityManagementService; import org.kuali.rice.kim.api.services.KimApiServiceLocator; import org.kuali.rice.kns.document.MaintenanceDocument; import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; import org.kuali.rice.krad.bo.PersistableBusinessObject; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; /** * Implements business rules checks on the SecurityModel maintenance document */ public class SecurityModelRule extends MaintenanceDocumentRuleBase { protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelRule.class); private SecurityModel oldSecurityModel; private SecurityModel newSecurityModel; protected volatile static BusinessObjectService businessObjectService; public SecurityModelRule() { super(); } /** * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) */ @Override protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) { boolean isValid = super.processCustomApproveDocumentBusinessRules(document); if (!isValid) { return isValid; } boolean isMaintenanceEdit = document.isEdit(); isValid &= validateSecurityModel(isMaintenanceEdit); return isValid; } /** * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) */ @Override protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { boolean isValid = super.processCustomRouteDocumentBusinessRules(document); if (!isValid) { return isValid; } boolean isMaintenanceEdit = document.isEdit(); isValid &= validateSecurityModel(isMaintenanceEdit); return isValid; } /** * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, * java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject) */ @Override public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) { boolean isValid = super.processCustomAddCollectionLineBusinessRules(document, collectionName, line); if (!isValid) { return isValid; } if (SecPropertyConstants.MODEL_DEFINITIONS.equals(collectionName)) { isValid &= validateModelDefinition((SecurityModelDefinition) line, ""); } if (SecPropertyConstants.MODEL_MEMBERS.equals(collectionName)) { isValid &= validateModelMember((SecurityModelMember) line, ""); } return isValid; } /** * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects() */ @Override public void setupConvenienceObjects() { oldSecurityModel = (SecurityModel) super.getOldBo(); newSecurityModel = (SecurityModel) super.getNewBo(); } /** * Validates the new security model record * * @param isMaintenanceEdit boolean indicating whether the maintenance action is an edit (true), or a new/copy (false) * @return boolean true if validation was successful, false if there are errors */ protected boolean validateSecurityModel(boolean isMaintenanceEdit) { boolean isValid = true; if (!isMaintenanceEdit) { boolean validModelName = verifyModelNameIsUnique(newSecurityModel, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE); if (!validModelName) { isValid = false; } } // check to make sure there is at least one model definition if (newSecurityModel.getModelDefinitions() == null || newSecurityModel.getModelDefinitions().size() == 0) { GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, SecKeyConstants.ERROR_MODEL_DEFINITION_MISSING); } int index = 0; for (SecurityModelDefinition modelDefinition : newSecurityModel.getModelDefinitions()) { String errorKeyPrefix = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_DEFINITIONS + "[" + index + "]."; boolean modelDefinitionValid = validateModelDefinition(modelDefinition, errorKeyPrefix); if (!modelDefinitionValid) { isValid = false; } index++; } index = 0; for (SecurityModelMember modelMember : newSecurityModel.getModelMembers()) { String errorKeyPrefix = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + SecPropertyConstants.MODEL_MEMBERS + "[" + index + "]."; boolean modelMemberValid = validateModelMember(modelMember, errorKeyPrefix); if (!modelMemberValid) { isValid = false; } index++; } return isValid; } /** * For new or copy action verifies the name given for the model is not being used by another model or definition * * @param securityModel SecurityModel with name to check * @param errorKeyPrefix String errorPrefix to use if any errors are found * @return boolean true if name exists, false if not */ protected boolean verifyModelNameIsUnique(SecurityModel securityModel, String errorKeyPrefix) { boolean isValid = true; Map<String, String> searchValues = new HashMap<String, String>(); searchValues.put(KFSPropertyConstants.NAME, securityModel.getName()); int matchCount = getBusinessObjectService().countMatching(SecurityModel.class, searchValues); if (matchCount > 0) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + KFSPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName()); isValid = false; } matchCount = getBusinessObjectService().countMatching(SecurityDefinition.class, searchValues); if (matchCount > 0) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + KFSPropertyConstants.NAME, SecKeyConstants.ERROR_MODEL_NAME_NON_UNIQUE, securityModel.getName()); isValid = false; } return isValid; } /** * Validates a definition assignment to the model * * @param modelDefinition SecurityModelDefinition to validate * @param errorKeyPrefix String errorPrefix to use if any errors are found * @return boolean true if validation was successful, false if there are errors */ protected boolean validateModelDefinition(SecurityModelDefinition modelDefinition, String errorKeyPrefix) { boolean isValid = true; modelDefinition.refreshNonUpdateableReferences(); if (ObjectUtils.isNull(modelDefinition.getSecurityDefinition())) { return false; } String attributeName = modelDefinition.getSecurityDefinition().getSecurityAttribute().getName(); String attributeValue = modelDefinition.getAttributeValue(); // if value is blank (which is allowed) no need to validate if (StringUtils.isBlank(attributeValue)) { return true; } // descend attributes do not allow multiple values or wildcards, and operator must be equal if (SecConstants.SecurityAttributeNames.CHART_DESCEND_HIERARCHY.equals(attributeName) || SecConstants.SecurityAttributeNames.ORGANIZATION_DESCEND_HIERARCHY.equals(attributeName)) { if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.MULTI_VALUE_SEPERATION_CHARACTER)) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_MULTI_ATTR_VALUE, attributeName); isValid = false; } if (StringUtils.contains(attributeValue, SecConstants.SecurityValueSpecialCharacters.WILDCARD_CHARACTER)) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.ATTRIBUTE_VALUE, SecKeyConstants.ERROR_MODEL_DEFINITION_WILDCARD_ATTR_VALUE, attributeName); isValid = false; } if (!SecConstants.SecurityDefinitionOperatorCodes.EQUAL.equals(modelDefinition.getOperatorCode())) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.OPERATOR_CODE, SecKeyConstants.ERROR_MODEL_DEFINITION_OPERATOR_CODE_NOT_EQUAL, attributeName); isValid = false; } } // validate attribute value for existence isValid = isValid && SecurityValidationUtil.validateAttributeValue(attributeName, attributeValue, errorKeyPrefix); return isValid; } /** * Validates a member assignment to the model * * @param modelMember SecurityModelMember to validate * @param errorKeyPrefix String errorPrefix to use if any errors are found * @return boolean true if validation was successful, false if there are errors */ protected boolean validateModelMember(SecurityModelMember modelMember, String errorKeyPrefix) { boolean isValid = true; String memberId = modelMember.getMemberId(); String memberTypeCode = modelMember.getMemberTypeCode(); if (StringUtils.isBlank(memberId) || StringUtils.isBlank(memberTypeCode)) { return false; } if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) { Principal principalInfo = KimApiServiceLocator.getIdentityService().getPrincipal(memberId); if (principalInfo == null) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode); isValid = false; } } else if (MemberType.ROLE.getCode().equals(memberTypeCode)) { Role roleInfo = KimApiServiceLocator.getRoleService().getRole(memberId); if (roleInfo == null) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode); isValid = false; } } else if (MemberType.GROUP.getCode().equals(memberTypeCode)) { Group groupInfo = KimApiServiceLocator.getGroupService().getGroup(memberId); if (groupInfo == null) { GlobalVariables.getMessageMap().putError(errorKeyPrefix + SecPropertyConstants.MEMBER_ID, SecKeyConstants.ERROR_MODEL_MEMBER_ID_NOT_VALID, memberId, memberTypeCode); isValid = false; } } return isValid; } /** * @return the default implementation of the business object service */ protected BusinessObjectService getBusinessObjectService() { if (businessObjectService == null) { businessObjectService = SpringContext.getBean(BusinessObjectService.class); } return businessObjectService; } }