1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.container.servlet.filters.internal

File SavedRequestRestorerFilter.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

16
37
10
2
262
119
22
0.59
3.7
5
2.2

Classes

Class Line # Actions
SavedRequestRestorerFilter 67 20 0% 11 14
0.588235358.8%
SavedRequestRestorerFilter.SavedRequestWrapper 86 17 0% 11 10
0.655172465.5%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.container.servlet.filters.internal;
21   
22    import java.io.IOException;
23    import java.util.Collections;
24    import java.util.Enumeration;
25    import java.util.HashMap;
26    import java.util.Map;
27    import java.util.regex.Matcher;
28    import java.util.regex.Pattern;
29   
30    import javax.servlet.Filter;
31    import javax.servlet.FilterChain;
32    import javax.servlet.FilterConfig;
33    import javax.servlet.ServletException;
34    import javax.servlet.ServletRequest;
35    import javax.servlet.ServletResponse;
36    import javax.servlet.http.HttpServletRequest;
37    import javax.servlet.http.HttpServletRequestWrapper;
38    import javax.servlet.http.HttpSession;
39   
40    import org.apache.commons.lang3.StringUtils;
41    import org.xwiki.container.servlet.filters.SavedRequestManager;
42    import org.xwiki.container.servlet.filters.SavedRequestManager.SavedRequest;
43   
44    /**
45    * <p>
46    * A filter that allows requests to be saved and reused later. For example when the current request contains an expired
47    * authentication token, and the authorization module redirects to the login page, all the information sent by the
48    * client would be lost; this filter allows to save all that information, and after a successful login, injects the
49    * saved data in the new request.
50    * </p>
51    * <p>
52    * The saved data is used as a fallback for the new request, meaning that a parameter value is first searched in the new
53    * request, and only if not found it is searched in the saved request. Only the request parameters are stored, along
54    * with the request URL needed to verify that the request is reused only in a compatible future request. Multiple
55    * requests can be stored, each one identified by a distinct ID. A request is restored only if a valid ID was provided
56    * in the new URL, and if the new URL matches the URL of the saved request (except the query string). A saved session is
57    * deleted after it is restored, so it cannot be reused more than once.
58    * </p>
59    * <p>
60    * Request data is stored in the current HTTP session, in order to provide a safe temporary storage. The data is only as
61    * safe as a session is, and it will not be available after the session is invalidated. Another consequence is that only
62    * HTTP requests are saved.
63    * </p>
64    *
65    * @version $Id: 497f09f8e284d2a251ea7326c89046f1d0bcf61f $
66    */
 
67    public class SavedRequestRestorerFilter implements Filter
68    {
69    /**
70    * Regular expression used for extracting the SRID from the query string. See
71    * {@link #getSavedRequest(HttpServletRequest)}.
72    */
73    private static final Pattern SAVED_REQUEST_REGEXP =
74    Pattern.compile("(?:^|&)" + SavedRequestManager.getSavedRequestIdentifier() + "=([^&]++)");
75   
76    /**
77    * The name of the request attribute that specifies if this filter has already been applied to the current request.
78    * This flag is required to prevent prevent processing the same request multiple times. The value of this request
79    * attribute is a string. The associated boolean value is determined using {@link Boolean#valueOf(String)}.
80    */
81    private static final String ATTRIBUTE_APPLIED = SavedRequestRestorerFilter.class.getName() + ".applied";
82   
83    /**
84    * Request Wrapper that inserts data from a previous request into the current request.
85    */
 
86    public static class SavedRequestWrapper extends HttpServletRequestWrapper
87    {
88    /** The saved request data; may be <code>null</code>, in which case no fallback data is used. */
89    private SavedRequest savedRequest;
90   
91    /**
92    * Simple constructor that forwards the initialization to the default {@link HttpServletRequestWrapper}. No
93    * saved data is used.
94    *
95    * @param request the new request, the primary object wrapped which contains the actual request data.
96    */
 
97  0 toggle public SavedRequestWrapper(HttpServletRequest request)
98    {
99  0 super(request);
100    }
101   
102    /**
103    * Constructor that forwards the new request to the {@link HttpServletRequestWrapper}, and stores the saved
104    * request data internally.
105    *
106    * @param newRequest the new request, the primary object wrapped which contains the actual request data.
107    * @param savedRequest the old request, the secondary object wrapped which contains the saved (fallback) request
108    * parameters.
109    */
 
110  12400 toggle public SavedRequestWrapper(HttpServletRequest newRequest, SavedRequest savedRequest)
111    {
112  12399 super(newRequest);
113  12391 this.savedRequest = savedRequest;
114    }
115   
116    /**
117    * Retrieves the value for the parameter, either from the new request, or from the saved data.
118    *
119    * @param name the name of the parameter
120    * @return a <code>String</code> representing the first value of the parameter, or <code>null</code> if no value
121    * was set in either of the requests.
122    * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
123    */
 
124  203352 toggle @Override
125    public String getParameter(String name)
126    {
127  203345 String value = super.getParameter(name);
128  203333 if (value == null && this.savedRequest != null) {
129  0 value = this.savedRequest.getParameter(name);
130    }
131  203336 return value;
132    }
133   
134    /**
135    * Retrieves all the values for the parameter, either from the new request, or from the saved data (but not
136    * combined).
137    *
138    * @param name the name of the parameter
139    * @return an array of <code>String</code> objects containing the parameter's values, or <code>null</code> if no
140    * value was set in either of the requests.
141    * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
142    */
 
143  15011 toggle @Override
144    public String[] getParameterValues(String name)
145    {
146  15028 String[] values = super.getParameterValues(name);
147  15048 if (values == null && this.savedRequest != null) {
148  0 values = this.savedRequest.getParameterValues(name);
149    }
150  15047 return values;
151    }
152   
153    /**
154    * Retrieves the combined map of parameter names - values, with the new values overriding the old ones.
155    *
156    * @return an immutable Map containing parameter names as keys and parameter values as map values
157    * @see javax.servlet.ServletRequest#getParameterMap()
158    */
 
159  11168 toggle @SuppressWarnings("unchecked")
160    @Override
161    public Map<String, String[]> getParameterMap()
162    {
163  11198 if (this.savedRequest == null) {
164  11195 return super.getParameterMap();
165    } else {
166    // First put the saved (old) request data in the map, so that the new data overrides it.
167  0 Map<String, String[]> map = new HashMap<String, String[]>(this.savedRequest.getParameterMap());
168  0 map.putAll(super.getParameterMap());
169  0 return Collections.unmodifiableMap(map);
170    }
171    }
172   
173    /**
174    * Retrieves the combined list of parameter names, from both the new and saved requests.
175    *
176    * @return an <code>Enumeration</code> of <code>String</code> objects, each <code>String</code> containing the
177    * name of a request parameter; or an empty <code>Enumeration</code> if the request has no parameters
178    * @see javax.servlet.ServletRequest#getParameterNames()
179    */
 
180  9933 toggle @Override
181    public Enumeration<String> getParameterNames()
182    {
183  9948 return Collections.enumeration(getParameterMap().keySet());
184    }
185    }
186   
 
187  32 toggle @Override
188    public void init(FilterConfig filterConfig)
189    {
190    // Don't do anything, as this filter does not need any resources.
191    }
192   
 
193  12583 toggle @Override
194    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
195    ServletException
196    {
197  12582 ServletRequest filteredRequest = request;
198    // This filter works only for HTTP requests, because they are the only ones with a session.
199  12541 if (request instanceof HttpServletRequest
200    && !Boolean.valueOf((String) request.getAttribute(ATTRIBUTE_APPLIED))) {
201    // Get the saved request, if any (returns null if not applicable)
202  12343 SavedRequest savedRequest = getSavedRequest((HttpServletRequest) request);
203    // Merge the new and the saved request
204  12372 filteredRequest = new SavedRequestWrapper((HttpServletRequest) request, savedRequest);
205  12366 filteredRequest.setAttribute(ATTRIBUTE_APPLIED, "true");
206    }
207    // Forward the request
208  12602 chain.doFilter(filteredRequest, response);
209    // Allow multiple calls to this filter as long as they are not nested.
210  12607 filteredRequest.removeAttribute(ATTRIBUTE_APPLIED);
211    }
212   
 
213  32 toggle @Override
214    public void destroy()
215    {
216    // Don't do anything, as this filter does not use any resources.
217    }
218   
219    /**
220    * If this request specifies a saved request (using the srid paramter) and the URL matches the one of the saved
221    * request, return the SavedRequest and remove it from the session.
222    *
223    * @param request the current request
224    * @return the saved request, if one exists, or <code>null</code>.
225    */
 
226  12285 toggle @SuppressWarnings("unchecked")
227    protected SavedRequest getSavedRequest(HttpServletRequest request)
228    {
229    // Only do something if the new request contains a Saved Request IDentifier (srid)
230  12319 String savedRequestId = null;
231    // Using request.getParameter is not good, since in some containers it prevents using request.getInputStream
232    // and/or request.getReader. A workaround is to manually extract the srid parameter from the query string, but
233    // this means that:
234    // - the srid cannot be used in POST requests, but in all current use cases GET is used anyway;
235    // - the regular expression used for this is pretty basic, so there might be some URLs that fail to be
236    // recognized; so far this wasn't observed.
237  12359 Matcher m = SAVED_REQUEST_REGEXP.matcher(StringUtils.defaultString(request.getQueryString()));
238  12373 if (m.find()) {
239  0 savedRequestId = m.group(1);
240    }
241   
242  12366 if (!StringUtils.isEmpty(savedRequestId)) {
243    // Saved requests are stored in the request session
244  0 HttpSession session = request.getSession();
245    // Get the SavedRequest from the session
246  0 Map<String, SavedRequest> savedRequests =
247    (Map<String, SavedRequest>) session.getAttribute(SavedRequestManager.getSavedRequestKey());
248  0 if (savedRequests != null) {
249  0 SavedRequest savedRequest = savedRequests.get(savedRequestId);
250    // Only reuse this request if the new request is for the same resource (URL)
251  0 if (savedRequest != null
252    && StringUtils.equals(savedRequest.getRequestUrl(), request.getRequestURL().toString())) {
253    // Remove the saved request from the session
254  0 savedRequests.remove(savedRequestId);
255    // Return the SavedRequest
256  0 return savedRequest;
257    }
258    }
259    }
260  12366 return null;
261    }
262    }