View Javadoc

1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.captcha;
17  
18  import org.acegisecurity.securechannel.ChannelEntryPoint;
19  import org.acegisecurity.util.PortMapper;
20  import org.acegisecurity.util.PortMapperImpl;
21  import org.acegisecurity.util.PortResolver;
22  import org.acegisecurity.util.PortResolverImpl;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import org.springframework.beans.factory.InitializingBean;
28  
29  import org.springframework.util.Assert;
30  
31  import java.io.IOException;
32  import java.io.UnsupportedEncodingException;
33  
34  import java.net.URLEncoder;
35  
36  import java.util.Enumeration;
37  
38  import javax.servlet.ServletException;
39  import javax.servlet.ServletRequest;
40  import javax.servlet.ServletResponse;
41  import javax.servlet.http.HttpServletRequest;
42  import javax.servlet.http.HttpServletResponse;
43  
44  
45  /***
46   * The captcha entry point : redirect to the captcha test page. <br>
47   * 
48   * <p>
49   * This entry point can force the use of SSL : see {@link #getForceHttps()}<br>
50   * </p>
51   * This entry point allows internal OR external redirect : see {@link #setOutsideWebApp(boolean)}<br>
52   * / Original request can be added to the redirect path using a custom
53   * translation : see {@link #setIncludeOriginalRequest(boolean)}<br>
54   * Original request is translated using URLEncoding and the following
55   * translation mapping in the redirect url :
56   * 
57   * <ul>
58   * <li>
59   * original url => {@link #getOriginalRequestUrlParameterName()}
60   * </li>
61   * <li>
62   * If {@link #isIncludeOriginalParameters()}
63   * </li>
64   * <li>
65   * original method => {@link #getOriginalRequestMethodParameterName()}
66   * </li>
67   * <li>
68   * original parameters => {@link #getOriginalRequestParametersParameterName()}
69   * </li>
70   * <li>
71   * The original parameters string is contructed using :
72   * 
73   * <ul>
74   * <li>
75   * a parameter separator {@link #getOriginalRequestParametersSeparator()}
76   * </li>
77   * <li>
78   * a parameter name value pair separator for each parameter {@link
79   * #getOriginalRequestParametersNameValueSeparator()}
80   * </li>
81   * </ul>
82   * 
83   * </li>
84   * </ul>
85   * 
86   * <br><br>
87   * Default values :<br>
88   * forceHttps = false<br>
89   * includesOriginalRequest = true<br>
90   * includesOriginalParameters = false<br>
91   * isOutsideWebApp=false<br>
92   * originalRequestUrlParameterName  =original_requestUrl <br>
93   * originalRequestParametersParameterName = original_request_parameters<br>
94   * originalRequestParametersNameValueSeparator =   __ <br>
95   * originalRequestParametersSeparator =  ;; <br>
96   * originalRequestMethodParameterName =  original_request_method    <br>
97   * urlEncodingCharset = UTF-8<br>
98   *
99   * @author marc antoine Garrigue
100  * @version $Id: CaptchaEntryPoint.java,v 1.4 2005/11/17 00:55:49 benalex Exp $
101  */
102 public class CaptchaEntryPoint implements ChannelEntryPoint, InitializingBean {
103     //~ Static fields/initializers =============================================
104 
105     // ~ Static fields/initializers
106     // =============================================
107     private static final Log logger = LogFactory.getLog(CaptchaEntryPoint.class);
108 
109     //~ Instance fields ========================================================
110 
111     // ~ Instance fields
112     // ========================================================
113     private PortMapper portMapper = new PortMapperImpl();
114     private PortResolver portResolver = new PortResolverImpl();
115     private String captchaFormUrl;
116     private String originalRequestMethodParameterName = "original_request_method";
117     private String originalRequestParametersNameValueSeparator = "__";
118     private String originalRequestParametersParameterName = "original_request_parameters";
119     private String originalRequestParametersSeparator = ";;";
120     private String originalRequestUrlParameterName = "original_requestUrl";
121     private String urlEncodingCharset = "UTF-8";
122     private boolean forceHttps = false;
123     private boolean includeOriginalParameters = false;
124     private boolean includeOriginalRequest = true;
125     private boolean isOutsideWebApp = false;
126 
127     //~ Methods ================================================================
128 
129     /***
130      * The URL where the <code>CaptchaProcessingFilter</code> login page can be
131      * found. Should be relative to the web-app context path, and include a
132      * leading <code>/</code>
133      *
134      * @param captchaFormUrl
135      */
136     public void setCaptchaFormUrl(String captchaFormUrl) {
137         this.captchaFormUrl = captchaFormUrl;
138     }
139 
140     /***
141      * DOCUMENT ME!
142      *
143      * @return the captcha test page to redirect to.
144      */
145     public String getCaptchaFormUrl() {
146         return captchaFormUrl;
147     }
148 
149     // ~ Methods
150     // ================================================================
151 
152     /***
153      * Set to true to force captcha form access to be via https. If this value
154      * is ture (the default is false), and the incoming request for the
155      * protected resource which triggered the interceptor was not already
156      * <code>https</code>, then
157      *
158      * @param forceHttps
159      */
160     public void setForceHttps(boolean forceHttps) {
161         this.forceHttps = forceHttps;
162     }
163 
164     public boolean getForceHttps() {
165         return forceHttps;
166     }
167 
168     public void setIncludeOriginalParameters(boolean includeOriginalParameters) {
169         this.includeOriginalParameters = includeOriginalParameters;
170     }
171 
172     public boolean isIncludeOriginalParameters() {
173         return includeOriginalParameters;
174     }
175 
176     /***
177      * If set to true, the original request url will be appended to the
178      * redirect url using the {@link #getOriginalRequestUrlParameterName()}.
179      *
180      * @param includeOriginalRequest
181      */
182     public void setIncludeOriginalRequest(boolean includeOriginalRequest) {
183         this.includeOriginalRequest = includeOriginalRequest;
184     }
185 
186     public boolean isIncludeOriginalRequest() {
187         return includeOriginalRequest;
188     }
189 
190     public void setOriginalRequestMethodParameterName(
191         String originalRequestMethodParameterName) {
192         this.originalRequestMethodParameterName = originalRequestMethodParameterName;
193     }
194 
195     public String getOriginalRequestMethodParameterName() {
196         return originalRequestMethodParameterName;
197     }
198 
199     public void setOriginalRequestParametersNameValueSeparator(
200         String originalRequestParametersNameValueSeparator) {
201         this.originalRequestParametersNameValueSeparator = originalRequestParametersNameValueSeparator;
202     }
203 
204     public String getOriginalRequestParametersNameValueSeparator() {
205         return originalRequestParametersNameValueSeparator;
206     }
207 
208     public void setOriginalRequestParametersParameterName(
209         String originalRequestParametersParameterName) {
210         this.originalRequestParametersParameterName = originalRequestParametersParameterName;
211     }
212 
213     public String getOriginalRequestParametersParameterName() {
214         return originalRequestParametersParameterName;
215     }
216 
217     public void setOriginalRequestParametersSeparator(
218         String originalRequestParametersSeparator) {
219         this.originalRequestParametersSeparator = originalRequestParametersSeparator;
220     }
221 
222     public String getOriginalRequestParametersSeparator() {
223         return originalRequestParametersSeparator;
224     }
225 
226     public void setOriginalRequestUrlParameterName(
227         String originalRequestUrlParameterName) {
228         this.originalRequestUrlParameterName = originalRequestUrlParameterName;
229     }
230 
231     public String getOriginalRequestUrlParameterName() {
232         return originalRequestUrlParameterName;
233     }
234 
235     /***
236      * if set to true, the {@link #commence(ServletRequest, ServletResponse)}
237      * method uses the {@link #getCaptchaFormUrl()} as a complete URL, else it
238      * as a 'inside WebApp' path.
239      *
240      * @param isOutsideWebApp
241      */
242     public void setOutsideWebApp(boolean isOutsideWebApp) {
243         this.isOutsideWebApp = isOutsideWebApp;
244     }
245 
246     public boolean isOutsideWebApp() {
247         return isOutsideWebApp;
248     }
249 
250     public void setPortMapper(PortMapper portMapper) {
251         this.portMapper = portMapper;
252     }
253 
254     public PortMapper getPortMapper() {
255         return portMapper;
256     }
257 
258     public void setPortResolver(PortResolver portResolver) {
259         this.portResolver = portResolver;
260     }
261 
262     public PortResolver getPortResolver() {
263         return portResolver;
264     }
265 
266     public void setUrlEncodingCharset(String urlEncodingCharset) {
267         this.urlEncodingCharset = urlEncodingCharset;
268     }
269 
270     public String getUrlEncodingCharset() {
271         return urlEncodingCharset;
272     }
273 
274     public void afterPropertiesSet() throws Exception {
275         Assert.hasLength(captchaFormUrl, "captchaFormUrl must be specified");
276         Assert.hasLength(originalRequestMethodParameterName,
277             "originalRequestMethodParameterName must be specified");
278         Assert.hasLength(originalRequestParametersNameValueSeparator,
279             "originalRequestParametersNameValueSeparator must be specified");
280         Assert.hasLength(originalRequestParametersParameterName,
281             "originalRequestParametersParameterName must be specified");
282         Assert.hasLength(originalRequestParametersSeparator,
283             "originalRequestParametersSeparator must be specified");
284         Assert.hasLength(originalRequestUrlParameterName,
285             "originalRequestUrlParameterName must be specified");
286         Assert.hasLength(urlEncodingCharset,
287             "urlEncodingCharset must be specified");
288         Assert.notNull(portMapper, "portMapper must be specified");
289         Assert.notNull(portResolver, "portResolver must be specified");
290         URLEncoder.encode("   fzaef é& à ", urlEncodingCharset);
291     }
292 
293     public void commence(ServletRequest request, ServletResponse response)
294         throws IOException, ServletException {
295         StringBuffer redirectUrl = new StringBuffer();
296         HttpServletRequest req = (HttpServletRequest) request;
297 
298         if (isOutsideWebApp) {
299             redirectUrl = redirectUrl.append(captchaFormUrl);
300         } else {
301             buildInternalRedirect(redirectUrl, req);
302         }
303 
304         if (includeOriginalRequest) {
305             includeOriginalRequest(redirectUrl, req);
306         }
307 
308         // add post parameter? DONE!
309         if (logger.isDebugEnabled()) {
310             logger.debug("Redirecting to: " + redirectUrl);
311         }
312 
313         ((HttpServletResponse) response).sendRedirect(redirectUrl.toString());
314     }
315 
316     private void buildInternalRedirect(StringBuffer redirectUrl,
317         HttpServletRequest req) {
318         // construct it
319         StringBuffer simpleRedirect = new StringBuffer();
320 
321         String scheme = req.getScheme();
322         String serverName = req.getServerName();
323         int serverPort = portResolver.getServerPort(req);
324         String contextPath = req.getContextPath();
325         boolean includePort = true;
326 
327         if ("http".equals(scheme.toLowerCase()) && (serverPort == 80)) {
328             includePort = false;
329         }
330 
331         if ("https".equals(scheme.toLowerCase()) && (serverPort == 443)) {
332             includePort = false;
333         }
334 
335         simpleRedirect.append(scheme);
336         simpleRedirect.append("://");
337         simpleRedirect.append(serverName);
338 
339         if (includePort) {
340             simpleRedirect.append(":");
341             simpleRedirect.append(serverPort);
342         }
343 
344         simpleRedirect.append(contextPath);
345         simpleRedirect.append(captchaFormUrl);
346 
347         if (forceHttps && req.getScheme().equals("http")) {
348             Integer httpPort = new Integer(portResolver.getServerPort(req));
349             Integer httpsPort = (Integer) portMapper.lookupHttpsPort(httpPort);
350 
351             if (httpsPort != null) {
352                 if (httpsPort.intValue() == 443) {
353                     includePort = false;
354                 } else {
355                     includePort = true;
356                 }
357 
358                 redirectUrl.append("https://");
359                 redirectUrl.append(serverName);
360 
361                 if (includePort) {
362                     redirectUrl.append(":");
363                     redirectUrl.append(httpsPort);
364                 }
365 
366                 redirectUrl.append(contextPath);
367                 redirectUrl.append(captchaFormUrl);
368             } else {
369                 redirectUrl.append(simpleRedirect);
370             }
371         } else {
372             redirectUrl.append(simpleRedirect);
373         }
374     }
375 
376     private void includeOriginalRequest(StringBuffer redirectUrl,
377         HttpServletRequest req) {
378         // add original request to the url
379         if (redirectUrl.indexOf("?") >= 0) {
380             redirectUrl.append("&");
381         } else {
382             redirectUrl.append("?");
383         }
384 
385         redirectUrl.append(originalRequestUrlParameterName);
386         redirectUrl.append("=");
387 
388         try {
389             redirectUrl.append(URLEncoder.encode(req.getRequestURL().toString(),
390                     urlEncodingCharset));
391         } catch (UnsupportedEncodingException e) {
392             logger.warn(e);
393         }
394 
395         //append method
396         redirectUrl.append("&");
397         redirectUrl.append(originalRequestMethodParameterName);
398         redirectUrl.append("=");
399         redirectUrl.append(req.getMethod());
400 
401         if (includeOriginalParameters) {
402             // append query params
403             redirectUrl.append("&");
404             redirectUrl.append(originalRequestParametersParameterName);
405             redirectUrl.append("=");
406 
407             StringBuffer qp = new StringBuffer();
408             Enumeration parameters = req.getParameterNames();
409 
410             if ((parameters != null) && parameters.hasMoreElements()) {
411                 //qp.append("?");
412                 while (parameters.hasMoreElements()) {
413                     String name = parameters.nextElement().toString();
414                     String value = req.getParameter(name);
415                     qp.append(name);
416                     qp.append(originalRequestParametersNameValueSeparator);
417                     qp.append(value);
418 
419                     if (parameters.hasMoreElements()) {
420                         qp.append(originalRequestParametersSeparator);
421                     }
422                 }
423             }
424 
425             try {
426                 redirectUrl.append(URLEncoder.encode(qp.toString(),
427                         urlEncodingCharset));
428             } catch (Exception e) {
429                 logger.warn(e);
430             }
431         }
432     }
433 }