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