View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.web.controller;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.krad.UserSession;
21  import org.kuali.rice.krad.uif.UifConstants;
22  import org.kuali.rice.krad.uif.UifParameters;
23  import org.kuali.rice.krad.uif.util.ProcessLogger;
24  import org.kuali.rice.krad.uif.view.ViewModel;
25  import org.kuali.rice.krad.util.CsrfValidator;
26  import org.kuali.rice.krad.util.GlobalVariables;
27  import org.kuali.rice.krad.util.KRADUtils;
28  import org.kuali.rice.krad.web.form.HistoryManager;
29  import org.kuali.rice.krad.web.form.UifFormBase;
30  import org.kuali.rice.krad.web.form.UifFormManager;
31  import org.kuali.rice.krad.web.service.ModelAndViewService;
32  import org.springframework.beans.factory.annotation.Autowired;
33  import org.springframework.web.bind.annotation.RequestMethod;
34  import org.springframework.web.method.HandlerMethod;
35  import org.springframework.web.servlet.HandlerInterceptor;
36  import org.springframework.web.servlet.ModelAndView;
37  
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  /**
42   * Spring controller intercepter for KRAD controllers.
43   *
44   * <p>Provides infrastructure for preparing the form and view before and after the controller is invoked.
45   * Included in this is form session management and preparation of the view for rendering</p>
46   *
47   * @author Kuali Rice Team (rice.collab@kuali.org)
48   */
49  public class UifControllerHandlerInterceptor implements HandlerInterceptor {
50      private static final Logger LOG = Logger.getLogger(UifControllerHandlerInterceptor.class);
51  
52      @Autowired
53      private ModelAndViewService modelAndViewService;
54  
55      /**
56       * Before the controller executes the user session is set on GlobalVariables
57       * and messages are cleared, in addition setup for the history manager and a check on missing session
58       * forms is performed.
59       *
60       * {@inheritDoc}
61       */
62      @Override
63      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
64              Object handler) throws Exception {
65          checkHandlerMethodAccess(request, handler);
66  
67          if (!CsrfValidator.validateCsrf(request, response)) {
68              return false;
69          }
70  
71          final UserSession session = KRADUtils.getUserSessionFromRequest(request);
72  
73          GlobalVariables.setUserSession(session);
74          GlobalVariables.clear();
75  
76          createUifFormManagerIfNecessary(request);
77  
78          // add the HistoryManager for storing HistoryFlows to the session
79          if (request.getSession().getAttribute(UifConstants.HistoryFlow.HISTORY_MANAGER) == null) {
80              request.getSession().setAttribute(UifConstants.HistoryFlow.HISTORY_MANAGER, new HistoryManager());
81          }
82  
83          ProcessLogger.trace("pre-handle");
84  
85          return true;
86      }
87  
88      /**
89       * Checks whether access is allowed for the requested controller method.
90       *
91       * <p>First a check is done on the method to determine whether it contains the annotation
92       * {@link org.kuali.rice.krad.web.controller.MethodAccessible}. If so, access is allowed. If the
93       * annotation is not present, data from the posted view (if any) is referenced to determine
94       * whether the method was configured as an accessible method to call.</p>
95       *
96       * <p>If method access is not allowed, a {@link org.kuali.rice.krad.web.controller.MethodAccessException}
97       * is thrown.</p>
98       *
99       * @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 }