001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.kim.document.rule;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
020import org.kuali.rice.core.api.uif.RemotableAttributeError;
021import org.kuali.rice.core.api.util.RiceKeyConstants;
022import org.kuali.rice.kim.api.common.template.Template;
023import org.kuali.rice.kim.api.permission.Permission;
024import org.kuali.rice.kim.api.services.KimApiServiceLocator;
025import org.kuali.rice.kim.api.type.KimType;
026import org.kuali.rice.kim.impl.permission.GenericPermissionBo;
027import org.kuali.rice.kim.impl.permission.PermissionBo;
028import org.kuali.rice.kim.impl.permission.PermissionTemplateBo;
029import org.kuali.rice.kim.framework.permission.PermissionTypeService;
030import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
031import org.kuali.rice.kns.document.MaintenanceDocument;
032import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
033import org.kuali.rice.krad.util.GlobalVariables;
034import org.apache.commons.lang.StringUtils;
035
036import javax.xml.namespace.QName;
037import java.util.List;
038import java.util.Map;
039import java.util.regex.Matcher;
040import java.util.regex.Pattern;
041
042/**
043 * This is a description of what this class does - kellerj don't forget to fill this in. 
044 * 
045 * @author Kuali Rice Team (rice.collab@kuali.org)
046 *
047 */
048public class GenericPermissionMaintenanceDocumentRule extends MaintenanceDocumentRuleBase {
049        protected static final String DETAIL_VALUES_PROPERTY = "detailValues";
050    protected static final String NAMESPACE_CODE_PROPERTY = "namespaceCode";
051        protected static final String ERROR_MESSAGE_PREFIX = "error.document.kim.genericpermission.";
052        protected static final String ERROR_MISSING_TEMPLATE = ERROR_MESSAGE_PREFIX + "missingtemplate";
053        protected static final String ERROR_UNKNOWN_ATTRIBUTE = ERROR_MESSAGE_PREFIX + "unknownattribute";
054        protected static final String ERROR_ATTRIBUTE_VALIDATION = ERROR_MESSAGE_PREFIX + "attributevalidation";
055    protected static final String ERROR_NAMESPACE_AND_NAME_VALIDATION = ERROR_MESSAGE_PREFIX + "namespaceandnamevalidation";
056
057        
058        @Override
059        protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
060                boolean rulesPassed = super.processCustomRouteDocumentBusinessRules( document );
061                try {
062                        GenericPermissionBo perm = (GenericPermissionBo)getNewBo();
063            GenericPermissionBo orgBo = (GenericPermissionBo)getOldBo();
064                        rulesPassed &= validateDetailValuesFormat(perm.getDetailValues());
065            if(StringUtils.isNotBlank(perm.getNamespaceCode()) && StringUtils.isNotBlank(perm.getName()) && StringUtils.isBlank(perm.getId())){
066                rulesPassed &= validateNamespaceCodeAndName(perm.getNamespaceCode(), perm.getName());
067            }
068            // rule case for copy
069            if(StringUtils.isNotBlank(perm.getNamespaceCode()) &&
070               StringUtils.isNotBlank(perm.getName()) &&
071               StringUtils.isNotBlank(orgBo.getId()) &&
072               StringUtils.isNotBlank(perm.getId()) &&
073               !StringUtils.equals(orgBo.getId(), perm.getId())){
074                  rulesPassed &= validateNamespaceCodeAndName(perm.getNamespaceCode(), perm.getName());
075            }
076            // rule case for edit
077            if(StringUtils.isNotBlank(perm.getNamespaceCode()) &&
078                    StringUtils.isNotBlank(perm.getName()) &&
079                    StringUtils.isNotBlank(orgBo.getId()) &&
080                    StringUtils.isNotBlank(perm.getId()) &&
081                    StringUtils.equals(orgBo.getId(), perm.getId()) &&
082                    ( !StringUtils.equals(orgBo.getNamespaceCode(), perm.getNamespaceCode()) ||
083                      !StringUtils.equals(orgBo.getName(), perm.getName())
084                    )
085               ) {
086                rulesPassed &= validateNamespaceCodeAndName(perm.getNamespaceCode(), perm.getName());
087            }
088                        // detailValues
089                        // get the type from the template for validation
090            Template template = null;
091            if(StringUtils.isNotBlank(perm.getTemplateId())){
092                 template =   KimApiServiceLocator.getPermissionService().getPermissionTemplate(perm.getTemplateId());
093                if ( template == null ) {
094                    GlobalVariables.getMessageMap().addToErrorPath( MAINTAINABLE_ERROR_PATH );
095                    GlobalVariables.getMessageMap().putError( DETAIL_VALUES_PROPERTY, ERROR_MISSING_TEMPLATE, perm.getTemplateId() );
096                    GlobalVariables.getMessageMap().removeFromErrorPath( MAINTAINABLE_ERROR_PATH );
097                    rulesPassed &= false;
098                } else {
099                    KimType kimType = KimApiServiceLocator.getKimTypeInfoService().getKimType(template.getKimTypeId());
100                    Map<String, String> details = perm.getDetails();
101                    // check that add passed attributes are defined
102                    for ( String attributeName : details.keySet() ) {
103                        if ( kimType.getAttributeDefinitionByName(attributeName) == null ) {
104                            GlobalVariables.getMessageMap().addToErrorPath( MAINTAINABLE_ERROR_PATH );
105                            GlobalVariables.getMessageMap().putError( DETAIL_VALUES_PROPERTY, ERROR_UNKNOWN_ATTRIBUTE, attributeName, template.getNamespaceCode(), template.getName() );
106                            GlobalVariables.getMessageMap().removeFromErrorPath( MAINTAINABLE_ERROR_PATH );
107                            rulesPassed &= false;
108                        }
109                    }
110                    // if all attributes are known, pass to the service for validation
111                    if ( !GlobalVariables.getMessageMap().hasErrors() ) {
112                        PermissionTypeService service = getPermissionTypeService( kimType.getServiceName() );
113                        if ( service != null ) {
114                            List<RemotableAttributeError> validationErrors = service.validateAttributes( kimType.getId(), details);
115                            if ( validationErrors != null && !validationErrors.isEmpty() ) {
116                                for ( RemotableAttributeError error : validationErrors ) {
117                                    GlobalVariables.getMessageMap().addToErrorPath( MAINTAINABLE_ERROR_PATH );
118                                    for (String errMsg : error.getErrors()) {
119                                        GlobalVariables.getMessageMap().putError( DETAIL_VALUES_PROPERTY, ERROR_ATTRIBUTE_VALIDATION, error.getAttributeName(), errMsg );
120                                    }
121                                    GlobalVariables.getMessageMap().removeFromErrorPath( MAINTAINABLE_ERROR_PATH );
122                                }
123                                rulesPassed &= false;
124                            }
125                        }
126                    }
127
128                }
129
130            }
131                        // check each permission name against the type
132                } catch ( RuntimeException ex ) {
133                        LOG.error( "Error in processCustomRouteDocumentBusinessRules()", ex );
134                        throw ex;
135                }
136                return rulesPassed;
137        }
138
139        protected boolean validateDetailValuesFormat(String permissionDetailValues){
140                if(permissionDetailValues != null){
141                        String spacesPattern = "[\\s\\t]*";
142                        Pattern pattern = Pattern.compile(".+"+"="+".+");
143                        Matcher matcher;
144                        // ensure that all line delimiters are single linefeeds
145                        permissionDetailValues = permissionDetailValues.replace( "\r\n", "\n" );
146                        permissionDetailValues = permissionDetailValues.replace( '\r', '\n' );
147                        if(StringUtils.isNotBlank(permissionDetailValues)){
148                                String[] values = permissionDetailValues.split( "\n" );
149                                for(String attrib: values){
150                                      matcher = pattern.matcher(attrib);
151                                      if(!matcher.matches()){
152                                          GlobalVariables.getMessageMap().putError(MAINTAINABLE_ERROR_PATH+"."+DETAIL_VALUES_PROPERTY, RiceKeyConstants.ERROR_INVALID_FORMAT, new String[]{"Detail Values", permissionDetailValues});
153                                          return false;
154                                      }
155                                }
156                        }
157                }
158                return true;
159        }
160    protected boolean validateNamespaceCodeAndName(String namespaceCode,String name){
161        Permission permission = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName(namespaceCode,name);
162        if(null != permission){
163            GlobalVariables.getMessageMap().putError(MAINTAINABLE_ERROR_PATH+"."+NAMESPACE_CODE_PROPERTY,ERROR_NAMESPACE_AND_NAME_VALIDATION,namespaceCode,name);
164            return false;
165        } else{
166            return true;
167        }
168    }
169        
170        protected PermissionTypeService getPermissionTypeService( String serviceName ) {
171        if ( StringUtils.isBlank( serviceName ) ) {
172                return null;
173        }
174        try {
175                Object service = GlobalResourceLoader.getService(QName.valueOf(serviceName));
176                // if we have a service name, it must exist
177                if ( service == null ) {
178                                LOG.warn("null returned for permission type service for service name: " + serviceName);
179                } else {
180                        // whatever we retrieved must be of the correct type
181                        if ( !(service instanceof PermissionTypeService)  ) {
182                                LOG.warn( "Service " + serviceName + " was not a KimPermissionTypeService.  Was: " + service.getClass().getName() );
183                                service = null;
184                        }
185                }
186                return (PermissionTypeService)service;
187        } catch( Exception ex ) {
188                LOG.error( "Error retrieving service: " + serviceName + " from the KimImplServiceLocator.", ex );
189        }
190        return null;
191    }
192
193}