Clover Coverage Report - Pebble 2.5-SNAPSHOT
Coverage timestamp: Sat Jun 12 2010 09:39:29 EST
../../../../../img/srcFileCovDistChart0.png 48% of files have more coverage
87   290   28   10,88
34   163   0,32   8
8     3,5  
1    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  HttpController       Line # 59 87 0% 28 129 0% 0.0
 
No Tests
 
1    /*
2    * Copyright (c) 2003-2006, Simon Brown
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * - Redistributions of source code must retain the above copyright
9    * notice, this list of conditions and the following disclaimer.
10    *
11    * - Redistributions in binary form must reproduce the above copyright
12    * notice, this list of conditions and the following disclaimer in
13    * the documentation and/or other materials provided with the
14    * distribution.
15    *
16    * - Neither the name of Pebble nor the names of its contributors may
17    * be used to endorse or promote products derived from this software
18    * without specific prior written permission.
19    *
20    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30    * POSSIBILITY OF SUCH DAMAGE.
31    */
32    package net.sourceforge.pebble.web.controller;
33   
34    import net.sourceforge.pebble.Constants;
35    import net.sourceforge.pebble.domain.AbstractBlog;
36    import net.sourceforge.pebble.domain.Blog;
37    import net.sourceforge.pebble.util.SecurityUtils;
38    import net.sourceforge.pebble.util.Utilities;
39    import net.sourceforge.pebble.web.action.Action;
40    import net.sourceforge.pebble.web.action.ActionFactory;
41    import net.sourceforge.pebble.web.action.ActionNotFoundException;
42    import net.sourceforge.pebble.web.action.SecureAction;
43    import net.sourceforge.pebble.web.model.Model;
44    import net.sourceforge.pebble.web.view.View;
45    import org.apache.commons.logging.Log;
46    import org.apache.commons.logging.LogFactory;
47   
48    import javax.servlet.ServletException;
49    import javax.servlet.http.*;
50    import java.io.IOException;
51    import java.security.SecureRandom;
52   
53    /**
54    * An implementation of the front controller pattern, using the command
55    * and controller strategy.
56    *
57    * @author Simon Brown
58    */
 
59    public class HttpController extends HttpServlet {
60   
61    /**
62    * the log used by this class
63    */
64    private static Log log = LogFactory.getLog(HttpController.class);
65   
66    /**
67    * the security token name
68    */
69    public static final String PEBBLE_SECURITY_TOKEN_PARAMETER = "pebbleSecurityToken";
70   
71    /**
72    * the header for bypassing security token checks
73    */
74    private static final String PEBBLE_SECURITY_TOKEN_HEADER = "X-Pebble-Token";
75   
76    /**
77    * the value the header should be for not checking
78    */
79    private static final String PEBBLE_SECURITY_TOKEN_HEADER_NOCHECK = "nocheck";
80   
81    /**
82    * a reference to the factory used to create Action instances
83    */
84    private ActionFactory actionFactory;
85   
86    /**
87    * the extension used to refer to actions
88    */
89    private String actionExtension = ".action";
90   
91    /**
92    * For generating secure tokens
93    */
94    private static final SecureRandom random = new SecureRandom();
95   
96    /**
97    * Initialises this instance.
98    */
 
99  0 toggle public void init() {
100  0 String actions = getServletConfig().getInitParameter("actions");
101  0 this.actionExtension = getServletConfig().getInitParameter("actionExtension");
102  0 this.actionFactory = new ActionFactory(actions);
103    }
104   
105    /**
106    * Processes the request - this is delegated to from doGet and doPost.
107    *
108    * @param request the HttpServletRequest instance
109    * @param response the HttpServletResponse instance
110    */
 
111  0 toggle protected void processRequest(HttpServletRequest request,
112    HttpServletResponse response)
113    throws ServletException, IOException {
114   
115  0 AbstractBlog blog = (AbstractBlog) request.getAttribute(Constants.BLOG_KEY);
116   
117    // find which action should be used
118  0 String actionName = request.getRequestURI();
119  0 if (actionName.indexOf("?") > -1) {
120    // strip of the query string - some servers leave this on
121  0 actionName = actionName.substring(0, actionName.indexOf("?"));
122    }
123  0 int index = actionName.lastIndexOf("/");
124  0 actionName = actionName.substring(index + 1, (actionName.length() - actionExtension.length()));
125  0 Action action;
126   
127  0 try {
128  0 log.debug("Action is " + actionName);
129  0 action = actionFactory.getAction(actionName);
130    } catch (ActionNotFoundException anfe) {
131  0 log.warn(anfe.getMessage());
132  0 response.sendError(HttpServletResponse.SC_NOT_FOUND);
133  0 return;
134    }
135   
136  0 boolean authorised = isAuthorised(request, action);
137  0 if (!authorised) {
138  0 response.sendError(HttpServletResponse.SC_FORBIDDEN);
139    } else {
140  0 boolean validated = validateSecurityToken(request, response, action);
141  0 if (!validated) {
142  0 response.sendError(HttpServletResponse.SC_FORBIDDEN, "No security token");
143    } else {
144  0 try {
145  0 Model model = new Model();
146  0 model.put(Constants.BLOG_KEY, blog);
147  0 String calculatedBaseUrl = Utilities.calcBaseUrl(request.getScheme(), blog.getUrl());
148  0 model.put(Constants.BLOG_URL, blog.getUrl());
149  0 action.setModel(model);
150  0 View view = action.process(request, response);
151  0 if (view != null) {
152   
153  0 view.setModel(model);
154  0 view.setServletContext(this.getServletContext());
155   
156  0 view.prepare();
157   
158  0 for (Object key : model.keySet()) {
159  0 request.setAttribute(key.toString(), model.get(key.toString()));
160    }
161   
162  0 response.setContentType(view.getContentType());
163  0 view.dispatch(request, response, getServletContext());
164   
165    }
166    } catch (Exception e) {
167  0 request.setAttribute("exception", e);
168  0 throw new ServletException(e);
169    }
170    }
171    }
172    }
173   
 
174  0 toggle private boolean isAuthorised(HttpServletRequest request, Action action) {
175  0 if (action instanceof SecureAction) {
176  0 SecureAction secureAction = (SecureAction) action;
177  0 return isUserInRole(request, secureAction);
178    } else {
179  0 return true;
180    }
181    }
182   
183    /**
184    * Determines whether the current user in one of the roles specified
185    * by the secure action.
186    *
187    * @param request the HttpServletRequest
188    * @param action the SecureAction to check against
189    * @return true if the user is in one of the roles, false otherwise
190    */
 
191  0 toggle private boolean isUserInRole(HttpServletRequest request, SecureAction action) {
192  0 AbstractBlog ab = (AbstractBlog) request.getAttribute(Constants.BLOG_KEY);
193  0 String currentUser = SecurityUtils.getUsername();
194  0 String roles[] = action.getRoles(request);
195  0 for (String role : roles) {
196  0 if (role.equals(Constants.ANY_ROLE)) {
197  0 return true;
198  0 } else if (SecurityUtils.isUserInRole(role)) {
199  0 if (ab instanceof Blog) {
200  0 Blog blog = (Blog) ab;
201  0 if (blog.isUserInRole(role, currentUser)) {
202  0 return true;
203    }
204    } else {
205  0 return true;
206    }
207    }
208    }
209  0 return false;
210    }
211   
 
212  0 toggle private boolean validateSecurityToken(HttpServletRequest request, HttpServletResponse response, Action action) {
213    // First, ensure that there is a security token, for future requests
214  0 String token = ensureSecurityTokenExists(request, response);
215  0 if (action.getClass().getAnnotation(RequireSecurityToken.class) != null) {
216    // Check for the header is there... XSRF attacks can't set custom headers, so if this header is there,
217    // it must be safe
218  0 if (PEBBLE_SECURITY_TOKEN_HEADER_NOCHECK.equals(request.getHeader(PEBBLE_SECURITY_TOKEN_HEADER))) {
219  0 return true;
220    }
221    // We must validate the token
222  0 String requestToken = request.getParameter(PEBBLE_SECURITY_TOKEN_PARAMETER);
223    // Compare token to cookie
224  0 return token.equals(requestToken);
225    } else {
226  0 return true;
227    }
228    }
229   
 
230  0 toggle private String ensureSecurityTokenExists(HttpServletRequest request, HttpServletResponse response) {
231  0 String token = (String) request.getAttribute(PEBBLE_SECURITY_TOKEN_PARAMETER);
232  0 if (token != null) {
233    // We've already configured it for this request
234  0 return token;
235    }
236  0 String contextPath = request.getContextPath();
237  0 for (Cookie cookie : request.getCookies()) {
238  0 if (HttpController.PEBBLE_SECURITY_TOKEN_PARAMETER.equals(cookie.getName())) {
239  0 if (contextPath != null && contextPath.length() > 0) {
240  0 if (contextPath.equals(cookie.getPath())) {
241  0 token = cookie.getValue();
242  0 break;
243    }
244    } else {
245  0 token = cookie.getValue();
246  0 break;
247    }
248    }
249    }
250    // No cookie, generate a token at least 12 characters long
251  0 if (token == null) {
252  0 token = "";
253  0 while (token.length() < 12) {
254  0 token += Long.toHexString(random.nextLong());
255    }
256    // Set the cookie
257  0 Cookie cookie = new Cookie(HttpController.PEBBLE_SECURITY_TOKEN_PARAMETER, token);
258    // Non persistent
259  0 cookie.setMaxAge(-1);
260  0 cookie.setPath(contextPath);
261  0 response.addCookie(cookie);
262    }
263    // Set it as a request attribute so the security token tag can find it
264  0 request.setAttribute(PEBBLE_SECURITY_TOKEN_PARAMETER, token);
265  0 return token;
266    }
267   
268    /**
269    * A default implementation of doGet that delegates to the processRequest method.
270    *
271    * @param req the HttpServletRequest instance
272    * @param res the HttpServletResponse instance
273    */
 
274  0 toggle protected void doGet(HttpServletRequest req, HttpServletResponse res)
275    throws ServletException, IOException {
276  0 processRequest(req, res);
277    }
278   
279    /**
280    * A default implementation of doPost that delegates to the processRequest method.
281    *
282    * @param req the HttpServletRequest instance
283    * @param res the HttpServletResponse instance
284    */
 
285  0 toggle protected void doPost(HttpServletRequest req, HttpServletResponse res)
286    throws ServletException, IOException {
287  0 processRequest(req, res);
288    }
289   
290    }