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.krad.web.controller; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.log4j.Logger; 020import org.kuali.rice.krad.UserSession; 021import org.kuali.rice.krad.uif.UifConstants; 022import org.kuali.rice.krad.uif.UifParameters; 023import org.kuali.rice.krad.uif.util.ProcessLogger; 024import org.kuali.rice.krad.uif.view.ViewModel; 025import org.kuali.rice.krad.util.CsrfValidator; 026import org.kuali.rice.krad.util.GlobalVariables; 027import org.kuali.rice.krad.util.KRADUtils; 028import org.kuali.rice.krad.web.form.HistoryManager; 029import org.kuali.rice.krad.web.form.UifFormBase; 030import org.kuali.rice.krad.web.form.UifFormManager; 031import org.kuali.rice.krad.web.service.ModelAndViewService; 032import org.springframework.beans.factory.annotation.Autowired; 033import org.springframework.web.bind.annotation.RequestMethod; 034import org.springframework.web.method.HandlerMethod; 035import org.springframework.web.servlet.HandlerInterceptor; 036import org.springframework.web.servlet.ModelAndView; 037 038import javax.servlet.http.HttpServletRequest; 039import javax.servlet.http.HttpServletResponse; 040 041/** 042 * Spring controller intercepter for KRAD controllers. 043 * 044 * <p>Provides infrastructure for preparing the form and view before and after the controller is invoked. 045 * Included in this is form session management and preparation of the view for rendering</p> 046 * 047 * @author Kuali Rice Team (rice.collab@kuali.org) 048 */ 049public class UifControllerHandlerInterceptor implements HandlerInterceptor { 050 private static final Logger LOG = Logger.getLogger(UifControllerHandlerInterceptor.class); 051 052 @Autowired 053 private ModelAndViewService modelAndViewService; 054 055 /** 056 * Before the controller executes the user session is set on GlobalVariables 057 * and messages are cleared, in addition setup for the history manager and a check on missing session 058 * forms is performed. 059 * 060 * {@inheritDoc} 061 */ 062 @Override 063 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 064 Object handler) throws Exception { 065 checkHandlerMethodAccess(request, handler); 066 067 if (!CsrfValidator.validateCsrf(request, response)) { 068 return false; 069 } 070 071 final UserSession session = KRADUtils.getUserSessionFromRequest(request); 072 073 GlobalVariables.setUserSession(session); 074 GlobalVariables.clear(); 075 076 createUifFormManagerIfNecessary(request); 077 078 // add the HistoryManager for storing HistoryFlows to the session 079 if (request.getSession().getAttribute(UifConstants.HistoryFlow.HISTORY_MANAGER) == null) { 080 request.getSession().setAttribute(UifConstants.HistoryFlow.HISTORY_MANAGER, new HistoryManager()); 081 } 082 083 ProcessLogger.trace("pre-handle"); 084 085 return true; 086 } 087 088 /** 089 * Checks whether access is allowed for the requested controller method. 090 * 091 * <p>First a check is done on the method to determine whether it contains the annotation 092 * {@link org.kuali.rice.krad.web.controller.MethodAccessible}. If so, access is allowed. If the 093 * annotation is not present, data from the posted view (if any) is referenced to determine 094 * whether the method was configured as an accessible method to call.</p> 095 * 096 * <p>If method access is not allowed, a {@link org.kuali.rice.krad.web.controller.MethodAccessException} 097 * is thrown.</p> 098 * 099 * @param request HTTP request (used to retrieve parameters) 100 * @param handler handler method that was determined based on the request and mappings 101 * @throws Exception 102 */ 103 protected void checkHandlerMethodAccess(HttpServletRequest request, Object handler) throws Exception { 104 String requestMethod = request.getMethod(); 105 106 // if it is a GET request then we allow without any check 107 if(requestMethod.equalsIgnoreCase(RequestMethod.GET.name())) { 108 return; 109 } 110 111 HandlerMethod handlerMethod = (HandlerMethod) handler; 112 MethodAccessible methodAccessible = handlerMethod.getMethodAnnotation(MethodAccessible.class); 113 114 // if accessible by annotation then return, otherwise go on to check view configuration 115 if (methodAccessible != null) { 116 return; 117 } 118 119 boolean isMethodAccessible = checkForMethodAccess(request); 120 121 if (!isMethodAccessible) { 122 throw new MethodAccessException(handlerMethod.getBeanType(), handlerMethod.getMethod().getName()); 123 } 124 } 125 126 /** 127 * Checks whether access to the handler method is allowed based available methods or accessible methods 128 * on view configuration. 129 * 130 * <p>Since this method is invoked before the request form is setup, we need to retrieve the session form 131 * form the form manager. In the case of missing post data (GET requests), view method access is not 132 * granted.</p> 133 * 134 * @param request HTTP request to retrieve parameters from 135 * @return boolean true if method access is allowed based on the view, false if not 136 */ 137 protected boolean checkForMethodAccess(HttpServletRequest request) { 138 String methodToCall = request.getParameter(UifParameters.METHOD_TO_CALL); 139 140 // if method to call is blank, we will assume they are using other strategies to map controller 141 // methods, and therefore using custom access management 142 if (StringUtils.isBlank(methodToCall)) { 143 return true; 144 } 145 146 UifFormManager uifFormManager = (UifFormManager) request.getSession().getAttribute(UifParameters.FORM_MANAGER); 147 UifFormBase form = null; 148 149 String formKeyParam = request.getParameter(UifParameters.FORM_KEY); 150 if (StringUtils.isNotBlank(formKeyParam) && (uifFormManager != null)) { 151 form = uifFormManager.getSessionForm(formKeyParam); 152 } 153 154 // if we don't have the view post data, there is nothing to validate 155 if ((form == null) || (form.getViewPostMetadata() == null)) { 156 return true; 157 } 158 159 // if the method to call is listed as a method in either the available methods to call or the 160 // view's accessible methods to call, then return true 161 return !form.getViewPostMetadata().getAvailableMethodToCalls().contains(methodToCall) || ((form 162 .getViewPostMetadata().getAccessibleMethodToCalls() != null) && form.getViewPostMetadata() 163 .getAccessibleMethodToCalls().contains(methodToCall)); 164 } 165 166 /** 167 * Checks if a form manager is present in the session, and if not creates a form manager and adds to the 168 * session and global variables. 169 * 170 * @param request http request being handled 171 */ 172 protected void createUifFormManagerIfNecessary(HttpServletRequest request) { 173 UifFormManager uifFormManager = (UifFormManager) request.getSession().getAttribute(UifParameters.FORM_MANAGER); 174 if (uifFormManager == null) { 175 uifFormManager = new UifFormManager(); 176 request.getSession().setAttribute(UifParameters.FORM_MANAGER, uifFormManager); 177 } 178 179 // add form manager to GlobalVariables for easy reference by other controller methods 180 GlobalVariables.setUifFormManager(uifFormManager); 181 } 182 183 /** 184 * After the controller logic is executed, the form is placed into session and the corresponding view 185 * is prepared for rendering. 186 * 187 * {@inheritDoc} 188 */ 189 @Override 190 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 191 ModelAndView modelAndView) throws Exception { 192 if (request.getAttribute(UifParameters.Attributes.VIEW_LIFECYCLE_COMPLETE) == null) { 193 getModelAndViewService().prepareView(request, modelAndView); 194 } 195 196 if ((modelAndView != null) && (modelAndView.getModelMap() != null)) { 197 Object model = modelAndView.getModelMap().get(UifConstants.DEFAULT_MODEL_NAME); 198 if ((model != null) && (model instanceof ViewModel)) { 199 ((ViewModel) model).preRender(request); 200 } 201 } 202 203 ProcessLogger.trace("post-handle"); 204 } 205 206 /** 207 * After the view is rendered remove the view to reduce the size of the form storage in memory. 208 * 209 * {@inheritDoc} 210 */ 211 @Override 212 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, 213 Exception ex) throws Exception { 214 ProcessLogger.trace("after-completion"); 215 216 UifFormManager uifFormManager = (UifFormManager) request.getSession().getAttribute(UifParameters.FORM_MANAGER); 217 UifFormBase uifForm = (UifFormBase) request.getAttribute(UifConstants.REQUEST_FORM); 218 219 if ((uifForm == null) || (uifForm.getView() == null)) { 220 return; 221 } 222 223 // remove the session transient variables from the request form before adding it to the list of 224 // Uif session forms 225 boolean persistFormToSession = uifForm.getView().isPersistFormToSession(); 226 if (persistFormToSession && (uifFormManager != null)) { 227 uifFormManager.purgeForm(uifForm); 228 uifFormManager.addSessionForm(uifForm); 229 } 230 231 uifForm.setView(null); 232 233 ProcessLogger.trace("after-completion-end"); 234 } 235 236 protected ModelAndViewService getModelAndViewService() { 237 return modelAndViewService; 238 } 239 240 public void setModelAndViewService(ModelAndViewService modelAndViewService) { 241 this.modelAndViewService = modelAndViewService; 242 } 243}