001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.captcha;
017:
018: import org.acegisecurity.securechannel.ChannelEntryPoint;
019:
020: import org.acegisecurity.util.PortMapper;
021: import org.acegisecurity.util.PortMapperImpl;
022: import org.acegisecurity.util.PortResolver;
023: import org.acegisecurity.util.PortResolverImpl;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import org.springframework.beans.factory.InitializingBean;
029:
030: import org.springframework.util.Assert;
031:
032: import java.io.IOException;
033: import java.io.UnsupportedEncodingException;
034:
035: import java.net.URLEncoder;
036:
037: import java.util.Enumeration;
038:
039: import javax.servlet.ServletException;
040: import javax.servlet.ServletRequest;
041: import javax.servlet.ServletResponse;
042: import javax.servlet.http.HttpServletRequest;
043: import javax.servlet.http.HttpServletResponse;
044:
045: /**
046: * The captcha entry point : redirect to the captcha test page.
047: * <p>
048: * This entry point can force the use of SSL : see {@link #getForceHttps()}
049: * </p>
050: * <p>
051: * This entry point allows internal OR external redirect : see {@link #setOutsideWebApp(boolean)}<br />
052: * / Original request can be added to the redirect path using a custom translation : see
053: * {@link #setIncludeOriginalRequest(boolean)}<br />
054: * The original request is translated using URLEncoding and the following translation mapping in the redirect url :
055: * <ul>
056: * <li>original url => {@link #getOriginalRequestUrlParameterName()}</li>
057: * <li>If {@link #isIncludeOriginalParameters()}</li>
058: * <li>original method => {@link #getOriginalRequestMethodParameterName()}</li>
059: * <li>original parameters => {@link #getOriginalRequestParametersParameterName()}</li>
060: * <li>The original parameters string is contructed using :
061: * <ul>
062: * <li>a parameter separator {@link #getOriginalRequestParametersSeparator()}</li>
063: * <li>a parameter name value pair separator for each parameter {@link
064: * #getOriginalRequestParametersNameValueSeparator()}</li>
065: * </ul>
066: * </li>
067: * </ul>
068: * <br><br>
069: * Default values :
070: * <pre>
071: * forceHttps = false
072: * includesOriginalRequest = true
073: * includesOriginalParameters = false
074: * isOutsideWebApp = false
075: * originalRequestUrlParameterName = original_requestUrl
076: * originalRequestParametersParameterName = original_request_parameters
077: * originalRequestParametersNameValueSeparator = __
078: * originalRequestParametersSeparator = ;;
079: * originalRequestMethodParameterName = original_request_method
080: * urlEncodingCharset = UTF-8
081: * </pre>
082: * </p>
083: *
084: * @author marc antoine Garrigue
085: * @version $Id: CaptchaEntryPoint.java 1784 2007-02-24 21:00:24Z luke_t $
086: */
087: public class CaptchaEntryPoint implements ChannelEntryPoint,
088: InitializingBean {
089: //~ Static fields/initializers =====================================================================================
090:
091: private static final Log logger = LogFactory
092: .getLog(CaptchaEntryPoint.class);
093:
094: //~ Instance fields ================================================================================================
095:
096: // ~ Instance fields
097: // ========================================================
098: private PortMapper portMapper = new PortMapperImpl();
099: private PortResolver portResolver = new PortResolverImpl();
100: private String captchaFormUrl;
101: private String originalRequestMethodParameterName = "original_request_method";
102: private String originalRequestParametersNameValueSeparator = "__";
103: private String originalRequestParametersParameterName = "original_request_parameters";
104: private String originalRequestParametersSeparator = ";;";
105: private String originalRequestUrlParameterName = "original_requestUrl";
106: private String urlEncodingCharset = "UTF-8";
107: private boolean forceHttps = false;
108: private boolean includeOriginalParameters = false;
109: private boolean includeOriginalRequest = true;
110: private boolean isOutsideWebApp = false;
111:
112: //~ Methods ========================================================================================================
113:
114: public void afterPropertiesSet() throws Exception {
115: Assert.hasLength(captchaFormUrl,
116: "captchaFormUrl must be specified");
117: Assert.hasLength(originalRequestMethodParameterName,
118: "originalRequestMethodParameterName must be specified");
119: Assert
120: .hasLength(originalRequestParametersNameValueSeparator,
121: "originalRequestParametersNameValueSeparator must be specified");
122: Assert
123: .hasLength(originalRequestParametersParameterName,
124: "originalRequestParametersParameterName must be specified");
125: Assert.hasLength(originalRequestParametersSeparator,
126: "originalRequestParametersSeparator must be specified");
127: Assert.hasLength(originalRequestUrlParameterName,
128: "originalRequestUrlParameterName must be specified");
129: Assert.hasLength(urlEncodingCharset,
130: "urlEncodingCharset must be specified");
131: Assert.notNull(portMapper, "portMapper must be specified");
132: Assert.notNull(portResolver, "portResolver must be specified");
133: URLEncoder.encode(" fzaef é& à ", urlEncodingCharset);
134: }
135:
136: private void buildInternalRedirect(StringBuffer redirectUrl,
137: HttpServletRequest req) {
138: // construct it
139: StringBuffer simpleRedirect = new StringBuffer();
140:
141: String scheme = req.getScheme();
142: String serverName = req.getServerName();
143: int serverPort = portResolver.getServerPort(req);
144: String contextPath = req.getContextPath();
145: boolean includePort = true;
146:
147: if ("http".equals(scheme.toLowerCase()) && (serverPort == 80)) {
148: includePort = false;
149: }
150:
151: if ("https".equals(scheme.toLowerCase()) && (serverPort == 443)) {
152: includePort = false;
153: }
154:
155: simpleRedirect.append(scheme);
156: simpleRedirect.append("://");
157: simpleRedirect.append(serverName);
158:
159: if (includePort) {
160: simpleRedirect.append(":");
161: simpleRedirect.append(serverPort);
162: }
163:
164: simpleRedirect.append(contextPath);
165: simpleRedirect.append(captchaFormUrl);
166:
167: if (forceHttps && req.getScheme().equals("http")) {
168: Integer httpPort = new Integer(portResolver
169: .getServerPort(req));
170: Integer httpsPort = (Integer) portMapper
171: .lookupHttpsPort(httpPort);
172:
173: if (httpsPort != null) {
174: if (httpsPort.intValue() == 443) {
175: includePort = false;
176: } else {
177: includePort = true;
178: }
179:
180: redirectUrl.append("https://");
181: redirectUrl.append(serverName);
182:
183: if (includePort) {
184: redirectUrl.append(":");
185: redirectUrl.append(httpsPort);
186: }
187:
188: redirectUrl.append(contextPath);
189: redirectUrl.append(captchaFormUrl);
190: } else {
191: redirectUrl.append(simpleRedirect);
192: }
193: } else {
194: redirectUrl.append(simpleRedirect);
195: }
196: }
197:
198: public void commence(ServletRequest request,
199: ServletResponse response) throws IOException,
200: ServletException {
201: StringBuffer redirectUrl = new StringBuffer();
202: HttpServletRequest req = (HttpServletRequest) request;
203:
204: if (isOutsideWebApp) {
205: redirectUrl = redirectUrl.append(captchaFormUrl);
206: } else {
207: buildInternalRedirect(redirectUrl, req);
208: }
209:
210: if (includeOriginalRequest) {
211: includeOriginalRequest(redirectUrl, req);
212: }
213:
214: // add post parameter? DONE!
215: if (logger.isDebugEnabled()) {
216: logger.debug("Redirecting to: " + redirectUrl);
217: }
218:
219: ((HttpServletResponse) response).sendRedirect(redirectUrl
220: .toString());
221: }
222:
223: /**
224: * DOCUMENT ME!
225: *
226: * @return the captcha test page to redirect to.
227: */
228: public String getCaptchaFormUrl() {
229: return captchaFormUrl;
230: }
231:
232: public boolean getForceHttps() {
233: return forceHttps;
234: }
235:
236: public String getOriginalRequestMethodParameterName() {
237: return originalRequestMethodParameterName;
238: }
239:
240: public String getOriginalRequestParametersNameValueSeparator() {
241: return originalRequestParametersNameValueSeparator;
242: }
243:
244: public String getOriginalRequestParametersParameterName() {
245: return originalRequestParametersParameterName;
246: }
247:
248: public String getOriginalRequestParametersSeparator() {
249: return originalRequestParametersSeparator;
250: }
251:
252: public String getOriginalRequestUrlParameterName() {
253: return originalRequestUrlParameterName;
254: }
255:
256: public PortMapper getPortMapper() {
257: return portMapper;
258: }
259:
260: public PortResolver getPortResolver() {
261: return portResolver;
262: }
263:
264: public String getUrlEncodingCharset() {
265: return urlEncodingCharset;
266: }
267:
268: private void includeOriginalRequest(StringBuffer redirectUrl,
269: HttpServletRequest req) {
270: // add original request to the url
271: if (redirectUrl.indexOf("?") >= 0) {
272: redirectUrl.append("&");
273: } else {
274: redirectUrl.append("?");
275: }
276:
277: redirectUrl.append(originalRequestUrlParameterName);
278: redirectUrl.append("=");
279:
280: try {
281: redirectUrl.append(URLEncoder.encode(req.getRequestURL()
282: .toString(), urlEncodingCharset));
283: } catch (UnsupportedEncodingException e) {
284: logger.warn(e);
285: }
286:
287: //append method
288: redirectUrl.append("&");
289: redirectUrl.append(originalRequestMethodParameterName);
290: redirectUrl.append("=");
291: redirectUrl.append(req.getMethod());
292:
293: if (includeOriginalParameters) {
294: // append query params
295: redirectUrl.append("&");
296: redirectUrl.append(originalRequestParametersParameterName);
297: redirectUrl.append("=");
298:
299: StringBuffer qp = new StringBuffer();
300: Enumeration parameters = req.getParameterNames();
301:
302: if ((parameters != null) && parameters.hasMoreElements()) {
303: //qp.append("?");
304: while (parameters.hasMoreElements()) {
305: String name = parameters.nextElement().toString();
306: String value = req.getParameter(name);
307: qp.append(name);
308: qp
309: .append(originalRequestParametersNameValueSeparator);
310: qp.append(value);
311:
312: if (parameters.hasMoreElements()) {
313: qp.append(originalRequestParametersSeparator);
314: }
315: }
316: }
317:
318: try {
319: redirectUrl.append(URLEncoder.encode(qp.toString(),
320: urlEncodingCharset));
321: } catch (Exception e) {
322: logger.warn(e);
323: }
324: }
325: }
326:
327: public boolean isIncludeOriginalParameters() {
328: return includeOriginalParameters;
329: }
330:
331: public boolean isIncludeOriginalRequest() {
332: return includeOriginalRequest;
333: }
334:
335: public boolean isOutsideWebApp() {
336: return isOutsideWebApp;
337: }
338:
339: /**
340: * The URL where the <code>CaptchaProcessingFilter</code> login page can be found. Should be relative to
341: * the web-app context path, and include a leading <code>/</code>
342: *
343: * @param captchaFormUrl
344: */
345: public void setCaptchaFormUrl(String captchaFormUrl) {
346: this .captchaFormUrl = captchaFormUrl;
347: }
348:
349: // ~ Methods
350: // ================================================================
351: /**
352: * Set to true to force captcha form access to be via https. If this value is ture (the default is false),
353: * and the incoming request for the protected resource which triggered the interceptor was not already
354: * <code>https</code>, then
355: *
356: * @param forceHttps
357: */
358: public void setForceHttps(boolean forceHttps) {
359: this .forceHttps = forceHttps;
360: }
361:
362: public void setIncludeOriginalParameters(
363: boolean includeOriginalParameters) {
364: this .includeOriginalParameters = includeOriginalParameters;
365: }
366:
367: /**
368: * If set to true, the original request url will be appended to the redirect url using the {@link
369: * #getOriginalRequestUrlParameterName()}.
370: *
371: * @param includeOriginalRequest
372: */
373: public void setIncludeOriginalRequest(boolean includeOriginalRequest) {
374: this .includeOriginalRequest = includeOriginalRequest;
375: }
376:
377: public void setOriginalRequestMethodParameterName(
378: String originalRequestMethodParameterName) {
379: this .originalRequestMethodParameterName = originalRequestMethodParameterName;
380: }
381:
382: public void setOriginalRequestParametersNameValueSeparator(
383: String originalRequestParametersNameValueSeparator) {
384: this .originalRequestParametersNameValueSeparator = originalRequestParametersNameValueSeparator;
385: }
386:
387: public void setOriginalRequestParametersParameterName(
388: String originalRequestParametersParameterName) {
389: this .originalRequestParametersParameterName = originalRequestParametersParameterName;
390: }
391:
392: public void setOriginalRequestParametersSeparator(
393: String originalRequestParametersSeparator) {
394: this .originalRequestParametersSeparator = originalRequestParametersSeparator;
395: }
396:
397: public void setOriginalRequestUrlParameterName(
398: String originalRequestUrlParameterName) {
399: this .originalRequestUrlParameterName = originalRequestUrlParameterName;
400: }
401:
402: /**
403: * if set to true, the {@link #commence(ServletRequest, ServletResponse)} method uses the {@link
404: * #getCaptchaFormUrl()} as a complete URL, else it as a 'inside WebApp' path.
405: *
406: * @param isOutsideWebApp
407: */
408: public void setOutsideWebApp(boolean isOutsideWebApp) {
409: this .isOutsideWebApp = isOutsideWebApp;
410: }
411:
412: public void setPortMapper(PortMapper portMapper) {
413: this .portMapper = portMapper;
414: }
415:
416: public void setPortResolver(PortResolver portResolver) {
417: this .portResolver = portResolver;
418: }
419:
420: public void setUrlEncodingCharset(String urlEncodingCharset) {
421: this.urlEncodingCharset = urlEncodingCharset;
422: }
423: }
|