You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2012/03/09 17:23:32 UTC
svn commit: r1298906 [12/14] - in /velocity/sandbox/velosurf: ./ docs/
examples/ examples/ant-vpp/ examples/ant-vpp/lib/ examples/auth-l10n/
examples/auth-l10n/WEB-INF/ examples/auth-l10n/WEB-INF/lib/
examples/auth-l10n/WEB-INF/src/ examples/auth-l10n/...
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/ValidationFilter.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/ValidationFilter.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/ValidationFilter.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/ValidationFilter.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.validation;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.velocity.velosurf.context.DBReference;
+
+//import org.apache.velocity.velosurf.util.UserContext;
+import org.apache.velocity.velosurf.context.EntityReference;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.ToolFinder;
+import org.apache.velocity.velosurf.util.UserContext;
+import org.apache.velocity.velosurf.web.VelosurfTool;
+import org.apache.velocity.velosurf.web.l10n.Localizer;
+
+/**
+ * <p>This class is an optional filter that will validate query data according to the <code>velosurf.entity</code> query parameter
+ * (may be multivalued).
+ * If data pass all validation constraints, the filter will let the request pass towards the form action. Otherwise,
+ * it will redirect back the user to the original form (using the referer query header). In this case, the filter will
+ * populate the session with the given values (escaped) so that they can be put back in the form.</p>
+ *
+ */
+public class ValidationFilter implements Filter
+{
+ /** filter config */
+ private FilterConfig config;
+
+ /** key used to identity an entity to be validated in HTTP parameters */
+ private static final String ENTITY_KEY = "velosurf.entity";
+
+ /**
+ * initialization.
+ * @param filterConfig filter config
+ * @throws ServletException
+ */
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ config = filterConfig;
+ }
+
+ /**
+ * Filtering.
+ * @param servletRequest request
+ * @param servletResponse response
+ * @param filterChain filter chain
+ * @throws IOException
+ * @throws ServletException
+ */
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest request = (HttpServletRequest)servletRequest;
+ HttpServletResponse response = (HttpServletResponse)servletResponse;
+ HttpSession session = null;
+ Map<String, Object> map = null;
+ String[] entities = null;
+ boolean filter = (request.getParameter(ENTITY_KEY) != null);
+ boolean accept = true;
+
+ if(filter)
+ {
+ session = request.getSession(true);
+
+ /*
+ * FIXME: the next call won't work when using a non-standard Velosurf config file
+ * defined in toolbox.xml
+ */
+ DBReference db = VelosurfTool.getDefaultInstance(config.getServletContext());
+
+ map = new HashMap();
+
+ UserContext userContext = (UserContext)session.getAttribute(UserContext.USER_CONTEXT_KEY);
+
+ if(userContext == null)
+ {
+ userContext = db.getUserContext();
+
+ Localizer localizer = ToolFinder.findSessionTool(session, Localizer.class);
+
+ if(localizer != null)
+ {
+ userContext.setLocalizer(localizer);
+ }
+ else
+ {
+ userContext.setLocale(request.getLocale());
+ }
+ session.setAttribute(UserContext.USER_CONTEXT_KEY, userContext);
+ }
+ else
+ {
+ db.setUserContext(userContext);
+ }
+ if(db != null)
+ {
+ Map<String, Object> params = request.getParameterMap();
+ Object[] array;
+
+ for(Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>)params.entrySet())
+ {
+ array = (Object[])entry.getValue();
+ map.put(entry.getKey(), array.length == 1 ? array[0] : array);
+ }
+ entities = (String[])params.get(ENTITY_KEY);
+ if(entities != null)
+ {
+ for(String entity : entities)
+ {
+ EntityReference entityRef = (EntityReference)db.get(entity);
+
+ if(entityRef == null)
+ {
+ Logger.error("validation: entity '" + entity + "' not found!");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "validation: entity '" + entity + "' does not exist!");
+ return;
+ }
+ else
+ {
+ try
+ {
+ accept &= entityRef.validate(map);
+ }
+ catch(Exception e)
+ {
+ Logger.error("validation: validation of entity '" + entity + "' throwed exception:");
+ Logger.log(e);
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "validation: validation of entity '" + entity
+ + "' throwed exception " + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Logger.error("validation: could not get a database connexion!");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Velosurf tool not found in session!");
+ return;
+ }
+ }
+ if(filter &&!accept)
+ {
+ Logger.trace("validation: values did not pass checking");
+
+ String referer = request.getHeader("Referer");
+
+ if(referer == null)
+ {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Referer header needed!");
+ }
+ else
+ {
+ for(String entity : entities)
+ {
+ session.setAttribute(entity, map);
+ }
+ Logger.trace("validation: redirecting request towards " + referer);
+ response.sendRedirect(referer);
+ }
+ }
+ else
+ {
+ if(filter /* && acept */)
+ {
+ Logger.trace("validation: values did pass checking");
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ /**
+ * Destroy the filter.
+ */
+ public void destroy(){}
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/package.html
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/package.html?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/package.html (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/package.html Fri Mar 9 16:23:25 2012
@@ -0,0 +1,23 @@
+<html>
+<body bgcolor="white">
+
+Contains all classes related to data validation constraints.
+
+<h2>Package Specification</h2>
+
+<!-- ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT GO THERE
+<ul>
+ <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+ <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/HttpQueryTool.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/HttpQueryTool.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/HttpQueryTool.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/HttpQueryTool.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web;
+
+import java.util.*;
+import org.apache.velocity.tools.view.context.ViewContext;
+import org.apache.velocity.tools.view.tools.ParameterParser;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>(Deprecated - please use org.apache.velocity.tools.view.ParameterTool)</p>
+ * This class extends the tool org.apache.velocity.tools.view.tools.ParameterParser,
+ * adding a generic setter. Values that are set manually hide any previous values that
+ * are present in the query under the same key.
+ *
+ * It is meant for the query scope.
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public @Deprecated
+class HttpQueryTool extends ParameterParser
+{
+ /** extra values map. */
+ private Map<String, Object> extraValues = new HashMap<String, Object>();
+
+ /**
+ * Constructor.
+ */
+ public HttpQueryTool(){}
+
+ /**
+ * Initialize this tool.
+ * @param viewContext view context
+ */
+ public void init(Object viewContext)
+ {
+ super.init(viewContext);
+
+ /* review all parameter keys to interpret "dots" inside keynames (only one level for now - FIXME: implement a recursive behaviour) */
+ for(Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>)getSource().entrySet())
+ {
+ String key = entry.getKey();
+ int dot = key.indexOf('.');
+
+ if(dot > 0 && dot < key.length() - 1)
+ {
+ String parentKey = key.substring(0, dot);
+ String subKey = key.substring(dot + 1);
+ Object value = entry.getValue();
+
+ if(value.getClass().isArray() && ((String[])value).length == 1)
+ {
+ value = ((String[])value)[0];
+ }
+
+ Map map = (Map)extraValues.get(parentKey);
+
+ if(map == null)
+ {
+ map = new HashMap();
+ extraValues.put(parentKey, map);
+ }
+ map.put(subKey, value);
+ }
+ }
+ }
+
+ /**
+ * Generic getter.
+ * @param key key
+ * @return value or null
+ */
+ public Object get(Object key)
+ {
+ Object ret = extraValues.get(key);
+
+ if(ret == null)
+ {
+ return super.get(key.toString());
+ }
+ else
+ {
+ return ret;
+ }
+ }
+
+ /**
+ * Generic getter with String argument (necessary to properly overload super getter).
+ * @param key key
+ * @return value or null
+ */
+ public Object get(String key)
+ {
+ return get((Object)key);
+ }
+
+ /**
+ * Generic setter.
+ * @param key key
+ * @param value value
+ * @return previous value
+ */
+ public Object put(String key, Object value)
+ {
+ return extraValues.put(key, value);
+ }
+
+ /**
+ * Get the number of parameters.
+ * @return number of parameters
+ */
+ public int size()
+ {
+ return getSource().size() + extraValues.size();
+ }
+
+ /**
+ * Check for the presence of parameters.
+ * @return true if empty
+ */
+ public boolean isEmpty()
+ {
+ return getSource().isEmpty() && extraValues.isEmpty();
+ }
+
+ /**
+ * Check for the presence of a parameter.
+ * @param key parameter name
+ * @return true if present
+ */
+ public boolean containsKey(Object key)
+ {
+ return getSource().containsKey(key) || extraValues.containsKey(key);
+ }
+
+ /**
+ * Check for the presence of a value.
+ * @param value value to find
+ * @return true if present
+ */
+ public boolean containsValue(Object value)
+ {
+ String[] array = new String[1];
+
+ array[0] = (String)value;
+ return getSource().containsValue(array) || extraValues.containsValue(value);
+ }
+
+ /**
+ * Remove a parameter (from extra values).
+ * @param key parameter name
+ * @return value or null
+ */
+ public Object remove(Object key)
+ {
+ Object inner = null, extra = null;
+
+ if(getSource().containsKey(key))
+ {
+ inner = getSource().get(key);
+ }
+ extra = extraValues.remove(key);
+ return inner != null ? inner : extra;
+ }
+
+ /**
+ * Put all key/values from a map.
+ * @param map source map
+ */
+ public void putAll(Map map)
+ {
+ extraValues.putAll(map);
+ }
+
+ /**
+ * Clear extra parameters.
+ */
+ public void clear()
+ {
+ extraValues.clear();
+ }
+
+ /**
+ * Get the set of parameter names.
+ * @return set of names
+ */
+ public Set keySet()
+ {
+ Set ret = new HashSet();
+
+ ret.addAll(getSource().keySet());
+ ret.addAll(extraValues.keySet());
+ return ret;
+ }
+
+ /**
+ * Get the collection of values
+ * @return collection of values
+ */
+ public Collection values()
+ {
+ String[] array;
+ Collection ret = new HashSet();
+ Collection coll = getSource().values();
+
+ for(Object value : coll)
+ {
+ if(value.getClass().isArray())
+ {
+ array = (String[])value;
+ ret.add(array.length == 1 ? (Object)array[0] : array);
+ }
+ else
+ {
+ ret.add(value);
+ }
+ }
+ ret.addAll(extraValues.values());
+ return ret;
+ }
+
+ public Set<Entry<String, Object>> entrySet()
+ {
+ Map map = new HashMap();
+ Set<Entry<String, Object>> coll = getSource().entrySet();
+
+ for(Entry entry : coll)
+ {
+ Object value = entry.getValue();
+
+ if(value.getClass().isArray() && ((String[])value).length == 1)
+ {
+ value = ((String[])value)[0];
+ }
+ map.put(entry.getKey(), value);
+ }
+
+ Set<Entry<String, Object>> ret = new HashSet(map.entrySet());
+
+ ret.addAll(extraValues.entrySet());
+ return ret;
+ }
+
+ public String toString()
+ {
+ StringBuilder ret = new StringBuilder("{ ");
+
+ for(Entry entry : entrySet())
+ {
+ ret.append(String.valueOf(entry.getKey()));
+ ret.append('=');
+ ret.append(String.valueOf(entry.getValue()));
+ ret.append(' ');
+ }
+ ret.append('}');
+ return ret.toString();
+ }
+
+ public Set<String> getExtraKeys()
+ {
+ return extraValues.keySet();
+ }
+
+ /**
+ * Debugging method: returns a query string corresponding to query parameters
+ * Warning: it also includes POST parameters (so strictly speaking
+ * it's not the real query string)
+ * @return reconstitued query string
+ */
+ public String getQueryString()
+ {
+ StringBuffer result = new StringBuffer();
+
+ for(Map.Entry entry : entrySet())
+ {
+ if(result.length() > 0)
+ {
+ result.append('&');
+ }
+ result.append(String.valueOf(entry.getKey()));
+ result.append('=');
+ result.append(String.valueOf(entry.getValue()));
+ }
+ return result.toString();
+ }
+
+ public Integer getInteger(String key)
+ {
+ Integer ret = super.getInteger(key);
+
+ /* try in extraValues */
+ if(ret == null)
+ {
+ Object v = extraValues.get(key);
+
+ if(v != null)
+ {
+ try
+ {
+ ret = Integer.parseInt(String.valueOf(v));
+ }
+ catch(NumberFormatException nfe) {}
+ }
+ }
+ return ret;
+ }
+
+ /* TODO subclass other getXXX() methods */
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/VelosurfTool.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/VelosurfTool.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/VelosurfTool.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/VelosurfTool.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import org.apache.velocity.tools.view.context.ViewContext;
+import org.apache.velocity.velosurf.context.DBReference;
+import org.apache.velocity.velosurf.sql.Database;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.ServletLogWriter;
+import org.apache.velocity.velosurf.util.ToolFinder;
+import org.apache.velocity.velosurf.util.UserContext;
+import org.apache.velocity.velosurf.util.XIncludeResolver;
+import org.apache.velocity.velosurf.web.l10n.Localizer;
+
+/**
+ * <p>This class is a tool meant to be referenced in toolbox.xml (VelocityTools View 1.4) or tools.xml (VelocityTools View 2.0+)</p>
+ * <p>It can be used in any scope you want (application/session/request), depending on the behaviour you need for the refinement and ordering mechanisms (which will follow the same scope).
+ * The initialization itself is very fast once all static initialization has been done, so there is no performance bottleneck when using request scope.</p>
+ * <p>You can have several instances of VelosurfTool, each with a distinct model configuration file.
+ * This can be useful to have one instance per schema, or one instance per database if dealing with several databases.</p>
+ * <p>For this to work, you have to use velocity-tools v1.3+, and give each instance the pathname of its configuration file
+ * via the 'config' parameter in the toolbox.xml file, like this for velocity-tools prior to 2.0:</p>
+ * <pre>
+ *
+ * <!-- first instance -->
+ * <tool>
+ * <key>db1</key>
+ * <scope>request</scope>
+ * <class>velosurf.tools.VelosurfTool</scope>
+ * <parameter name="config" value="WEB-INF/db1.xml" />
+ * </tool>
+ *
+ * <!-- second instance -->
+ * <tool>
+ * <key>db2</key>
+ * <scope>request</scope>
+ * <class>velosurf.tools.VelosurfTool</scope>
+ * <parameter name="config" value="WEB-INF/db2.xml" />
+ * </tool>
+ *
+ * </pre>
+ * <p>And like this for velocity-tools v2.0+ :</p>
+ * <pre>
+ *
+ * <toolbox scope="request">
+ * <!-- first instance -->
+ * <tool key="db1" class="velosurf.tools.VelosurfTool" config="WEB-INF/db1.xml"/>
+ * <!-- second instance -->
+ * <tool key="db2" class="velosurf.tools.VelosurfTool" config="WEB-INF/db2.xml"/>
+ * </toolbox>
+ * </pre>
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public class VelosurfTool extends DBReference
+{
+ /**
+ * build a new VelosurfTool.
+ */
+ public VelosurfTool(){}
+
+ /**
+ * Initialize this instance using the given ViewContext.
+ *
+ * @param viewContext initialization data
+ */
+ public void init(Object viewContext) throws Exception
+ {
+ // getservlet context
+ ViewContext viewctx = null;
+ ServletContext ctx = null;
+
+ if(viewContext instanceof ServletContext)
+ {
+ ctx = (ServletContext)viewContext;
+ }
+ else if(viewContext instanceof ViewContext)
+ {
+ viewctx = (ViewContext)viewContext;
+ ctx = viewctx.getServletContext();
+ }
+ else
+ {
+ Logger.error("Error: Initialization: no valid initialization data found: got a "
+ + viewContext.getClass().getName());
+ System.err.println("Error: Initialization: no valid initialization data found!");
+ }
+ if(!Logger.isInitialized() && ctx != null)
+ {
+ Logger.setWriter(new ServletLogWriter(ctx));
+ }
+
+ // get config file
+ if(configFile == null)
+ { // if not already given by configure()
+ configFile = findConfigFile(ctx);
+ }
+
+ UserContext userContext = null;
+
+ /* user context */
+ if(viewctx != null)
+ {
+ HttpSession session = viewctx.getRequest().getSession(false);
+
+ if(session != null)
+ {
+ synchronized(session)
+ {
+ userContext = (UserContext)session.getAttribute(UserContext.USER_CONTEXT_KEY);
+ if(userContext == null)
+ {
+ /* check in the database if already connected */
+ Database db = dbMap.get(configFile);
+
+ if(db != null)
+ {
+ userContext = db.getUserContext();
+ }
+ else
+ {
+ userContext = new UserContext();
+ }
+ session.setAttribute(UserContext.USER_CONTEXT_KEY, userContext);
+ if(fetchLocalizer)
+ {
+ Localizer localizer = ToolFinder.findSessionTool(session, Localizer.class);
+
+ if(localizer != null)
+ {
+ userContext.setLocalizer(localizer);
+ }
+ else
+ {
+ // don't search for it again
+ fetchLocalizer = false;
+ userContext.setLocale(viewctx.getRequest().getLocale());
+ }
+ }
+ else
+ {
+ userContext.setLocale(viewctx.getRequest().getLocale());
+ }
+ }
+ }
+ session.setAttribute(VelosurfTool.class.getName(), this);
+ }
+ }
+
+ /* initialize with a new or existing connection */
+ Database db = getConnection(configFile, ctx);
+
+ if(db == null)
+ {
+ throw new SQLException("Could not connect to database.");
+ }
+ else
+ {
+ db.setUserContext(userContext);
+ }
+ super.init(db);
+ }
+
+ /**
+ * Tries to find the configuration file.
+ * @param ctx servelt context
+ * @return found config file or null
+ */
+ static String findConfigFile(ServletContext ctx)
+ {
+ /* tries with a servlet context parameter */
+ String configFile = ctx.getInitParameter(WEBAPP_CONFIG_FILE_KEY);
+
+ if(configFile != null)
+ {
+// Logger.warn("Use of the "+WEBAPP_CONFIG_FILE_KEY+" servlet context parameter is deprecated.");
+// Logger.warn("Consider moving this parameter to toolbox.xml.");
+ return configFile;
+ }
+ configFile = ctx.getInitParameter(WEBAPP_CONFIG_FILE_KEY2);
+ if(configFile != null)
+ {
+// Logger.warn("Use of the "+WEBAPP_CONFIG_FILE_KEY+" servlet context parameter is deprecated.");
+// Logger.warn("Consider moving this parameter to toolbox.xml.");
+ return configFile;
+ }
+
+ // else try default
+ InputStream check = ctx.getResourceAsStream(DEFAULT_CONFIG_FILE);
+
+ if(check == null)
+ {
+ check = ctx.getResourceAsStream(OLD_DEFAULT_CONFIG_FILE);
+ if(check == null)
+ {
+ throw new RuntimeException(
+ "Velosurf config file not found! Please specify it using the servlet context or the toolbox parameters.");
+ }
+ else
+ {
+ configFile = OLD_DEFAULT_CONFIG_FILE;
+ }
+ }
+ else
+ {
+ configFile = DEFAULT_CONFIG_FILE;
+ }
+ return configFile;
+ }
+
+ /**
+ * initialization from a servlet context
+ *
+ */
+ protected void initialize(ServletContext ctx){}
+
+ /**
+ * key used in the deployment descriptor (web.xml) to set the name of the config file.
+ */
+ private static final String WEBAPP_CONFIG_FILE_KEY = "velosurf.config";
+
+ /**
+ * alternate webapp key
+ */
+ private static final String WEBAPP_CONFIG_FILE_KEY2 = "velosurf.configuration";
+
+ /**
+ * key used in the toolbox (toolbox.xml) to set the name of the config file.
+ */
+ private static final String TOOLBOX_CONFIG_FILE_KEY = "config";
+
+ /**
+ * alternate key for the toolbox
+ */
+ private static final String TOOLBOX_CONFIG_FILE_KEY2 = "configuration";
+
+ /**
+ * default database config file.
+ */
+ private static final String DEFAULT_CONFIG_FILE = "/WEB-INF/model.xml";
+
+ /**
+ * old default database config file.
+ */
+ private static final String OLD_DEFAULT_CONFIG_FILE = "/WEB-INF/velosurf.xml";
+
+ /**
+ * path to the config file.
+ */
+ private String configFile = null;
+
+ /**
+ * database connections.
+ */
+ private static Map<String, Database> dbMap = new HashMap<String, Database>();
+
+ /**
+ * configure.
+ *
+ * @param map parameters
+ */
+ public void configure(Map<String, String> map)
+ {
+ configFile = map.get(TOOLBOX_CONFIG_FILE_KEY);
+ if(configFile == null)
+ {
+ configFile = map.get(TOOLBOX_CONFIG_FILE_KEY2);
+ }
+ }
+
+ /**
+ * return the existing Database for the specified config file, or null
+ * if it isn't already open.
+ * @param configFile
+ * @return a Database
+ */
+ protected static Database getConnection(String configFile)
+ {
+ if(!configFile.startsWith("/"))
+ {
+ configFile = "/" + configFile;
+ }
+ return dbMap.get(configFile);
+ }
+
+ /**
+ * return a db reference on the existing Database for the specified config file, or null
+ * if it isn't already open.
+ * @param configFile
+ * @return a DBReference
+ */
+ public static DBReference getInstance(String configFile)
+ {
+ if(!configFile.startsWith("/"))
+ {
+ configFile = "/" + configFile;
+ }
+
+ Database db = dbMap.get(configFile);
+
+ return db == null ? null : new DBReference(db);
+ }
+
+ /**
+ * return the existing Database for the specified config file and servlet context,
+ * or null if an error occurs.
+ * @param configFile
+ * @return a Database
+ */
+ protected static Database getConnection(String configFile, ServletContext servletContext)
+ {
+ if(!configFile.startsWith("/"))
+ {
+ configFile = "/" + configFile;
+ }
+
+ Database db = (Database)dbMap.get(configFile);
+
+ if(db == null)
+ {
+ try
+ {
+ Logger.info("Using config file '" + configFile + "'");
+
+ InputStream is = servletContext.getResourceAsStream(configFile);
+
+ if(is == null)
+ {
+ Logger.error("Could not read config file " + configFile);
+ return null;
+ }
+
+ /* calculate the base directory, for XInclude */
+ /* Velosurf won't like '/' in windows names or '\' in linux ones... Does Java anyway? */
+ String base = null;
+
+ configFile = configFile.replace('\\', '/');
+
+ int i = configFile.lastIndexOf('/');
+
+ if(i == -1)
+ {
+ base = ".";
+ }
+ else
+ {
+ base = configFile.substring(0, i);
+ }
+ db = Database.getInstance(is, new XIncludeResolver(base, servletContext));
+ dbMap.put(configFile, db);
+ }
+ catch(Throwable e)
+ {
+ Logger.error("Could not get a connection!");
+ Logger.log(e);
+ }
+ }
+ return db;
+ }
+
+ /**
+ * return a db reference on the existing Database for the specified config file and servlet context,
+ * or null if an error occurs.
+ * @param configFile
+ * @return a DBReference
+ */
+ public static DBReference getInstance(String configFile, ServletContext servletContext)
+ {
+ Database db = getConnection(configFile, servletContext);
+
+ return db == null ? null : new DBReference(db);
+ }
+
+ /**
+ * return the existing Database for the default config file, or null
+ * if it does not already exist.
+ * @return a Database
+ */
+ protected static Database getDefaultConnection()
+ {
+ return(Database)dbMap.get(DEFAULT_CONFIG_FILE);
+ }
+
+ /**
+ * return a db reference the existing Database for the default config file, or null
+ * if it does not already exist.
+ * @return a DBReference
+ */
+ public static DBReference getDefaultInstance()
+ {
+ Database db = getDefaultConnection();
+
+ return db == null ? null : new DBReference(db);
+ }
+
+ /**
+ * return the existing Database for the default config file and servlet context,
+ * or null if an error occurs.
+ * @return a Database
+ */
+ protected static Database getDefaultConnection(ServletContext servletContext)
+ {
+ return getConnection(DEFAULT_CONFIG_FILE, servletContext);
+ }
+
+ /**
+ * return a db reference on the existing Database for the default config file
+ * or null if an error occurs.
+ * @return a Database
+ */
+ public static DBReference getDefaultInstance(ServletContext servletContext)
+ {
+ String configFile = findConfigFile(servletContext);
+
+ if(configFile == null)
+ {
+ throw new RuntimeException(
+ "VelosurfTool.getDefaultInstance: Configuration file not found! Please add a 'velosurf.config' servlet context parameter.");
+ }
+
+ Database db = getConnection(configFile, servletContext);
+
+ return db == null ? null : new DBReference(db);
+ }
+
+ /**
+ * do we need to try to fetch the localizer object?
+ * True initially, false after one unsuccessful try.
+ */
+ private static boolean fetchLocalizer = true;
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/AuthenticationFilter.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/AuthenticationFilter.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/AuthenticationFilter.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/AuthenticationFilter.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web.auth;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Locale;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.velocity.velosurf.util.*;
+import org.apache.velocity.velosurf.web.l10n.Localizer;
+
+/**
+ * <p>This class is a servlet filter used to protect web pages behind an authentication mechanism. When a
+ * non-authenticated user requests a private page, (s)he is redirected towards the login page and thereafter,
+ * if (s)he logged in successfully, towards his(her) initially requested page.</p>
+ *
+ * <p>Authentication is performed via a CRAM (challenge-response authentication mechanism).
+ * Passwords are encrypted using the method given as parameter to the Authenticator tool in toolbox.xml. The provided
+ * Javascript file /src/javascript/md5.js implements the HmacMD5 method on the client side.</p>
+ *
+ * <p>This filter works in conjunction with an Authenticator object that must be present in the session scope
+ * of the toolbox and with a javascript password encryption function.</p>
+ *
+ * <p>To use it, you just have to map private urls (and especially, the target of the login form, this is
+ * very important for the authentication to work properly!) to go through this filter, as in :</p>
+ * <xmp>
+ * <filter>
+ * <filter-name>authentication</filter-name>
+ * <filter-class>auth.AuthenticationFilter</filter-class>
+ * </filter>
+ * <filter-mapping>
+ * <filter-name>authentication</filter-name>
+ * <url-pattern>/auth/*</url-pattern>
+ * </filter-mapping>
+ * </xmp>
+ *
+ * <p>The password is encrypted in an irreversible manner into an <i>answer</i>, and to check the login,
+ * the answer that the client sends back to the server is compared to the correct awaited answer.</p>
+ *
+ * <p>The filter expect the login to be present in the HTTP 'login' form field, and the answer in
+ * the 'answer' form field (which should be all right if you use the login.vjs as is). The action of the form
+ * is never used (since the filter will redirect the user towards the page asked before the login), but <b>it must
+ * be catched by an url-pattern of this filter</b>. You can for instance define a mapping towards "/process_login".</p>
+ *
+ * <p>The logged state is materialized by the presence of a user Object in the session under
+ * the <i>user</i> key. This user object in the one returned by the abstract method Authenticator.getUser(login).</p>
+ *
+ * <p>This filter will search for an occurrence of a localizer tool in the session toolbox to resolve some values.
+ * The presence of this localizer is optional.</p>
+ *
+ * <p>Optional configuration parameters:
+ * <ul>
+ * <li><code>login-field</code>: name of the login form field (default: login).</li>
+ * <li><code>password-field</code>: name of the password field (default: password).</li>
+ * <li><code>login-key</code>: name of the session key used to store the login of the logged user.</li>
+ * <li><code>user-key</code>: name of the session key used to store a reference to the logged user object.</li>
+ * <li><code>max-inactive</code>: delay upon which an inactive user is disconnected in seconds.
+ * The default value is one hour.</li>
+ * <li><code>login-page</code>: the login page URI. The "<code>@</code>" pattern applies as well. Default is '/login.html'.</li>
+ * <li><code>authenticated-index-page</code>: the default page once authenticated. The "<code>@</code>" pattern applies as well.
+ * Default is '/logged.html'.</li>
+ * <li><code>bad-login-message</code>: the message to be displayed in case of bad login. If this parameter is not
+ * specified, the filter will try to get a reference from the localizer tool and ask it for a "badLogin"
+ * message, and if this fails, it will simply use "Bad login or password.".</li>
+ * <li><code>disconnected-message</code>: the message to be displayed when the user is disconnected after a period
+ * of inactivity on the site. Same remark if this parameter is not supplied: the filter will search
+ * for a "disconnected" message in the localizer tool if present, and otherwise display "You have been disconnected."</li>
+ * <li><code>allow-guest</code>: allow the login "guest" (false by default) - the password is not checked, the "guest" user must exist in the database.
+ * The only allowed uri is the login page, and it can be used from VTL:<br/> <code>#macro (redirect $url) $response.sendRedirect($url) #end #redirect("/login.do?login=guest&password=whatever")</code><br/>
+ * you can also include a "redirect=" parameter</li>
+ * <li><code>use-login-referer</code>: (sorry for reproducing the bugguy syntax from the HTTP RFC, it should be 'referrer' :-) ) use the HTTP request referrer after login if there is no saved request (default: no).</li>
+ * </ul>
+ * </p>
+ *
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ *
+ */
+
+public class AuthenticationFilter implements Filter
+{
+
+ /**
+ * Filter config.
+ */
+ private FilterConfig config = null;
+
+ /**
+ * Max inactive interval.
+ */
+ private int maxInactive = 3600;
+
+ /**
+ * Login field.
+ */
+ private String loginField = "login";
+
+ /**
+ * Password field.
+ */
+ private String passwordField = "password";
+
+ /**
+ * Login page.
+ */
+ private String loginPage = "/login.vhtml";
+
+ /**
+ * Index of the authenticated zone.
+ */
+ private String authenticatedIndexPage = "/index.vhtml";
+
+ /**
+ * Message in case of bad login.
+ */
+ private String badLoginMessage = null;
+
+ /**
+ * Message key in case of bad login.
+ */
+ private String badLoginMsgKey = "badLogin";
+
+ /**
+ * Default bad login message.
+ */
+ private static final String defaultBadLoginMessage = "Bad login or password.";
+
+ /**
+ * Message in case of disconnection.
+ */
+ private String disconnectedMessage = null;
+
+ /**
+ * Message key in case of disconnection.
+ */
+ private String disconnectedMsgKey = "disconnected";
+
+ /**
+ * Default message in case of disconnection.
+ */
+ private static final String defaultDisconnectedMessage = "You have been disconnected.";
+
+ /**
+ * Session key used to store logged user login
+ */
+ private String LOGIN = "velosurf.auth.login";
+
+ /**
+ * Session key used to store logged user object
+ */
+ private String USER = "velosurf.auth.user";
+
+ /**
+ * Session key used to store original pre-login request
+ */
+ public static final String REQUEST = "velosurf.auth.saved-request";
+
+ /**
+ * Should we use the referer to login.do?
+ */
+ private boolean useLoginReferer = false;
+
+ /**
+ * Should we use the referer to login.do?
+ */
+ private boolean allowGuest = false;
+
+ /**
+ * Initialization.
+ * @param config filter config
+ * @throws ServletException
+ */
+ public void init(FilterConfig config) throws ServletException
+ {
+ this.config = config;
+
+ /* logger initialization */
+ if (!Logger.isInitialized())
+ {
+ Logger.setWriter(new ServletLogWriter(config.getServletContext()));
+ }
+
+ /* max-inactive */
+ String param = this.config.getInitParameter("max-inactive");
+ if (param != null)
+ {
+ try
+ {
+ maxInactive = Integer.parseInt(param);
+ }
+ catch (NumberFormatException nfe)
+ {
+ Logger.error("AuthenticationFilter: bad format for the max-inactive parameter: "+param);
+ }
+ }
+
+ /* login field */
+ param = this.config.getInitParameter("login-field");
+ if (param != null)
+ {
+ loginField = param;
+ }
+
+ /* password field */
+ param = this.config.getInitParameter("password-field");
+ if (param != null)
+ {
+ passwordField = param;
+ }
+
+ /* login session key */
+ param = this.config.getInitParameter("login-key");
+ if (param != null)
+ {
+ LOGIN = param;
+ }
+
+ /* user session key */
+ param = this.config.getInitParameter("user-key");
+ if (param != null)
+ {
+ USER = param;
+ }
+
+ /* login page */
+ param = this.config.getInitParameter("login-page");
+ if (param != null)
+ {
+ loginPage = param;
+ }
+
+ /* authenticated index page */
+ param = this.config.getInitParameter("authenticated-index-page");
+ if (param != null)
+ {
+ authenticatedIndexPage = param;
+ }
+
+ /* bad login message */
+ badLoginMessage = this.config.getInitParameter("bad-login-message");
+
+ /* disconnected message */
+ disconnectedMessage = this.config.getInitParameter("disconnected-message");
+
+ /* use login referer */
+ param = this.config.getInitParameter("use-login-referer");
+ useLoginReferer = (param != null && Boolean.parseBoolean(param));
+
+ /* allow guests */
+ param = this.config.getInitParameter("allow-guest");
+ allowGuest = (param != null && Boolean.parseBoolean(param));
+
+ }
+
+ /**
+ * Filtering.
+ * @param servletRequest request
+ * @param servletResponse response
+ * @param chain filter chain
+ * @throws IOException
+ * @throws ServletException
+ */
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest request = (HttpServletRequest)servletRequest;
+ HttpSession session = request.getSession(false);
+ HttpServletResponse response = (HttpServletResponse)servletResponse;
+
+ String uri = request.getRequestURI();
+
+ String login = null, password = null;
+
+ if (session != null
+ && session.getId().equals(request.getRequestedSessionId()) /* not needed in theory */
+ && session.getAttribute(USER) != null)
+ {
+ /* already logged*/
+
+ /* need to refresh cached user instance in case it changed */
+ refreshUserInstance(session);
+
+ /* if asked to logout, well, logout! */
+ if (uri.endsWith("/logout.do"))
+ {
+ doLogout(request,response,chain);
+ }
+ else
+ {
+ doProcessAuthentified(request,response,chain);
+ }
+ }
+ else
+ {
+ /* never protect the login page itself */
+ if (uri.equals(resolveLocalizedUri(request,loginPage)))
+ {
+ chain.doFilter(request,response);
+ return;
+ }
+ if (session == null)
+ {
+ session = request.getSession(true);
+ }
+ else
+ {
+ /* clear any previous loginMessage */
+ session.removeAttribute("loginMessage");
+ }
+ session.removeAttribute(USER);
+ session.removeAttribute(LOGIN);
+ if ( uri.endsWith("/login.do")
+ && (login = request.getParameter(loginField)) != null
+ && (password = request.getParameter(passwordField)) != null
+ && session.getId().equals(request.getRequestedSessionId()))
+ {
+ // a user is trying to log in
+
+ if(allowGuest && login.equals("guest"))
+ { // TODO parametrize "guest" -> GUEST
+ Logger.trace("[auth] logging in guest user");
+ doLogin(request,response,chain);
+ }
+ else
+ {
+ // get a reference to the authenticator tool
+ BaseAuthenticator auth = ToolFinder.findSessionTool(session,BaseAuthenticator.class);
+ if (auth == null)
+ {
+ Logger.error("[auth] cannot find any reference to the authenticator tool in the session!");
+ /* Maybe the current user tried to validate an expired login form... well... ask him again... */
+ response.sendRedirect(resolveLocalizedUri(request,loginPage));
+ return;
+ }
+ // check answer
+ if (auth.checkLogin(login,password))
+ {
+ // login ok
+ doLogin(request,response,chain);
+ }
+ else
+ {
+ badLogin(request,response,chain);
+ }
+ }
+
+ }
+ else
+ {
+ /* do not redirect to the logout */
+ if (uri.endsWith("/logout.do"))
+ {
+ response.sendRedirect(resolveLocalizedUri(request,loginPage));
+ }
+ else
+ {
+ doRedirect(request,response,chain);
+ }
+ }
+ }
+ }
+
+ protected void refreshUserInstance(HttpSession session)
+ {
+ session.setAttribute(USER, ToolFinder.findSessionTool(session,BaseAuthenticator.class).getUser((String)session.getAttribute(LOGIN)));
+ }
+
+ protected void doRedirect(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ /* save the original request */
+ String uri = request.getRequestURI();
+ Logger.trace("[auth] saving request towards "+uri);
+ HttpSession session = request.getSession();
+ session.setAttribute(REQUEST,SavedRequest.saveRequest(request));
+
+ /* check to see if the current user has been disconnected
+ note that this test will fail when the servlet container
+ reuses session ids */
+ boolean disconnected = false;
+ String reqId = request.getRequestedSessionId();
+ if (reqId != null && (session == null || !session.getId().equals(reqId)))
+ {
+ disconnected = true;
+ }
+
+ if(disconnected)
+ {
+ String message = disconnectedMessage != null ?
+ disconnectedMessage :
+ getMessage(ToolFinder.findSessionTool(session,Localizer.class),disconnectedMsgKey,defaultDisconnectedMessage);
+ session.setAttribute("loginMessage",message);
+ }
+ // redirect to login page
+ String loginPage = resolveLocalizedUri(request,this.loginPage);
+ Logger.trace("[auth] redirecting unauthenticated user to "+loginPage);
+ response.sendRedirect(loginPage);
+ }
+
+ protected void doLogin(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ String login = request.getParameter(loginField);
+ Logger.info("[auth] user '"+login+"' successfully logged in.");
+ HttpSession session = request.getSession();
+ session.setAttribute(USER, ToolFinder.findSessionTool(session,BaseAuthenticator.class).getUser(login));
+ session.setAttribute(LOGIN,login);
+ if (maxInactive > 0)
+ {
+ Logger.trace("[auth] setting session max inactive interval to "+maxInactive);
+ session.setMaxInactiveInterval(maxInactive);
+ }
+ session.removeAttribute("challenge");
+ session.removeAttribute("authenticator");
+ // then handle the former request if not null
+ goodLogin(request,response,chain);
+ }
+
+ protected void goodLogin(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpSession session = request.getSession();
+
+ // trying to use the "redirect=" parameter
+ String redirect = request.getParameter("redirect");
+ Logger.trace("[auth] redirect = "+redirect);
+ if(redirect != null)
+ {
+ Logger.trace("[auth] redirecting newly logged user to 'redirect' param: "+redirect);
+ response.sendRedirect(redirect);
+ return;
+ }
+
+ SavedRequest savedRequest = (SavedRequest)session.getAttribute(REQUEST);
+ if (savedRequest == null || savedRequest.getRequestURI().endsWith("/login.do"))
+ {
+ // try to redirect to the referrer url
+ if(useLoginReferer)
+ {
+ String referer = request.getHeader("Referer");
+ if(referer != null)
+ { // TODO: some referer URLs should be avoided (login.do, logout.do...)
+ // only keep path and query
+ URL url = new URL(referer);
+ String path = url.getPath();
+ String query = url.getQuery();
+ String anchor = url.getRef();
+ String dest = path+(query != null && query.length()>0?"?"+query:"")+(anchor != null && anchor.length()>0?"#"+anchor:"");
+ Logger.trace("[auth] redirecting newly logged user to login.do referer: "+dest);
+ response.sendRedirect(dest);
+ return;
+ }
+ }
+
+ // redirect to /auth/index.html
+ String authIndex = resolveLocalizedUri(request,getAuthenticatedIndexPage(session));
+ Logger.trace("[auth] redirecting newly logged user to "+authIndex);
+ response.sendRedirect(authIndex);
+ }
+ else
+ {
+ session.removeAttribute(REQUEST);
+ String formerUrl = savedRequest.getRequestURI();
+ String query = savedRequest.getQueryString();
+ query = (query == null ? "" : "?"+query);
+ formerUrl += query;
+ Logger.trace("[auth] redirecting newly logged user to "+formerUrl);
+ response.sendRedirect(formerUrl);
+ }
+ }
+
+ protected void badLogin(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ Logger.warn("[auth] user " + request.getParameter(loginField) + " made an unsuccessfull login attempt.");
+ HttpSession session = request.getSession();
+ String message = badLoginMessage != null ?
+ badLoginMessage :
+ getMessage(ToolFinder.findSessionTool(session,Localizer.class),badLoginMsgKey,defaultBadLoginMessage);
+ session.setAttribute("loginMessage",message);
+ // redirect to login page
+ String loginPage = resolveLocalizedUri(request,this.loginPage);
+ Logger.trace("[auth] redirecting unauthenticated user to "+loginPage);
+ response.sendRedirect(loginPage);
+ }
+
+ protected void doProcessAuthentified(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ /* if the request is still pointing on /login.html or /login.do, redirect to /auth/index.html */
+ String uri = request.getRequestURI();
+ HttpSession session = request.getSession();
+ if (uri.equals(resolveLocalizedUri(request,loginPage)) || uri.endsWith("/login.do"))
+ {
+ goodLogin(request,response,chain);
+ }
+ else
+ {
+ Logger.trace("[auth] user is authenticated.");
+ SavedRequest saved = (SavedRequest)session.getAttribute(REQUEST);
+ if (saved != null && saved.getRequestURL().equals(request.getRequestURL()))
+ {
+ session.removeAttribute(REQUEST);
+ chain.doFilter(new SavedRequestWrapper(request,saved),response);
+ }
+ else
+ {
+ chain.doFilter(request,response);
+ }
+ }
+ }
+
+ protected void doLogout(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpSession session = request.getSession();
+ Logger.trace("[auth] user logged out");
+ session.removeAttribute(USER);
+ session.removeAttribute(LOGIN);
+ String loginPage = resolveLocalizedUri(request,this.loginPage);
+ response.sendRedirect(loginPage);
+ }
+
+
+ protected String resolveLocalizedUri(HttpServletRequest request,String uri)
+ {
+ if (uri.indexOf('@')!=-1)
+ {
+ /* means the uri need the current locale */
+ Locale locale = null;
+ HttpSession session = request.getSession();
+ if (session != null)
+ {
+ locale = (Locale)session.getAttribute("velosurf.l10n.active-locale"); /* TODO: gather 'active-locale' handling in HTTPLocalizerTool */
+ }
+
+ if (locale == null)
+ {
+ Logger.error("[auth] cannot find the active locale in the session!");
+ Logger.error("[auth] the LocalizationFilter must reside before this filter in the filters chain.");
+ //response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return uri;
+ }
+ uri = uri.replaceAll("@",locale.toString());
+ }
+ return uri;
+ }
+
+ protected String getAuthenticatedIndexPage(HttpSession session)
+ {
+ return authenticatedIndexPage;
+ }
+
+ /**
+ * Message getter.
+ * @param localizer localizer
+ * @param key key
+ * @param defaultMessage default message
+ * @return localized message or default message
+ */
+ protected String getMessage(Localizer localizer,String key,String defaultMessage)
+ {
+ String message = null;
+ if (localizer != null)
+ {
+ message = localizer.get(key);
+ }
+ return message == null || message.equals(key) ? defaultMessage : message;
+ }
+
+ /**
+ * Destroy the filter.
+ */
+ public void destroy()
+ {
+ }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/BaseAuthenticator.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/BaseAuthenticator.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/BaseAuthenticator.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/BaseAuthenticator.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web.auth;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Random;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.HttpSession;
+import org.apache.velocity.tools.view.context.ViewContext;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * This abstract class implements an authentication mechanism. It is meant to be declared
+ * in toolbox.xml as a session-scoped tool.
+ *
+ * The password encryption method can be specified in <code>toolbox.xml</code> using the <code>method</code> parameter
+ * (when not specified, passwords are passed in clear).
+ *
+ * You will need to implement the same password encryption on the client side using the adequate
+ * javascript files. A <code>/src/javascript/md5.js</code> file is provided to help implementing the HmacMD5 method.
+ *
+ * Still, if you really want security, use HTTPS!
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public abstract class BaseAuthenticator
+{
+ /**
+ * get the password corresponding to a login.
+ * @param login login
+ * @return password or null
+ */
+ public abstract String getPassword(String login);
+
+ /**
+ * Get the user object corresponding to a login
+ * @param login login
+ * @return user object
+ */
+ public abstract Object getUser(String login);
+
+ /** encryption method */
+ private String method = null;
+
+ /** challenge value */
+ private String challenge = null;
+
+ /** random number generator */
+ private static Random random = new Random(System.currentTimeMillis());
+
+ /** length of challenge */
+ private static final int CHALLENGE_LENGTH = 256; // bits
+
+ /** keep a reference on the session */
+ private WeakReference<HttpSession> session = null;
+
+ /**
+ * initialize this tool.
+ * @param initData a view context
+ */
+ public void init(Object initData)
+ {
+ if(!(initData instanceof ViewContext))
+ {
+ Logger.error("auth: authenticator tool should be used in a session scope! (received init data of class: "
+ + (initData == null ? "null" : initData.getClass().getName()) + ")");
+ }
+
+ HttpSession s = ((ViewContext)initData).getRequest().getSession(true);
+
+ session = new WeakReference<HttpSession>(s);
+ s.setAttribute(BaseAuthenticator.class.getName(), this);
+ }
+
+ /**
+ * configure this tool.
+ * @param config map containing an optional "method" parameter
+ */
+ public void configure(Map config)
+ {
+ method = (String)config.get("method");
+ }
+
+ /**
+ * This method generates a new challenge each time it is called.
+ *
+ * @return a new 1024-bit challenge in base64
+ */
+ public String getChallenge()
+ {
+ BigInteger bigint = new BigInteger(CHALLENGE_LENGTH, random);
+
+ challenge = new sun.misc.BASE64Encoder().encode(bigint.toByteArray());
+ challenge = challenge.replace("\n", "");
+ Logger.trace("auth: generated new challenge: " + challenge);
+ return challenge;
+ }
+
+ /**
+ * Check received answer.
+ *
+ * @param login login
+ * @param answer received answer
+ * @return true if received answer is valid
+ */
+ public boolean checkLogin(String login, String answer)
+ {
+ String password = getPassword(login);
+
+ if(password == null)
+ {
+ /* password not found */
+ Logger.trace("auth: login " + login + " does not exist");
+ return false;
+ }
+ if(password.length() == 0 && answer.length() == 0)
+ {
+ return true;
+ }
+
+ String correctAnswer = generateAnswer(password);
+
+ Logger.trace("auth: received=" + answer);
+ Logger.trace("auth: correct =" + correctAnswer);
+ return(correctAnswer != null && correctAnswer.equals(answer));
+ }
+
+ /**
+ * Generate the correct answer.
+ * @param password
+ * @return encrypted answer
+ */
+ private String generateAnswer(String password)
+ {
+ if(method == null)
+ {
+ return password;
+ }
+ else if(challenge == null)
+ {
+ /* return something that will never match any password */
+ return getChallenge();
+ }
+ else
+ {
+ Logger.debug("auth: using method " + method);
+ try
+ {
+ /*
+ * TODO: use utf8 (and find a way to convert an utf8 string into
+ * an array of bytes on the javascript counterpart)
+ */
+ Mac mac = Mac.getInstance(method);
+
+ mac.init(new SecretKeySpec(password.getBytes("ISO-8859-1"), method));
+
+ byte[] hash = mac.doFinal(challenge.getBytes("ISO-8859-1"));
+ String encoded = new BASE64Encoder().encode(hash);
+
+ /* strips the last(s) '=' */
+ int i;
+
+ while((i = encoded.lastIndexOf('=')) != -1)
+ {
+ encoded = encoded.substring(0, i);
+ }
+ return encoded;
+ }
+ catch(NoSuchAlgorithmException nsae)
+ {
+ Logger.error("auth: could not find algorithm '" + method + "'");
+ Logger.log(nsae);
+ }
+ catch(Exception e)
+ {
+ Logger.error("auth: an unknown error occurred...");
+ Logger.log(e);
+ }
+ }
+ return null;
+ }
+
+ public Object getLoggedUser()
+ {
+ if(session == null)
+ {
+ return null;
+ }
+
+ HttpSession sess = session.get();
+
+ if(sess == null)
+ {
+ return null;
+ }
+ return sess.getAttribute("velosurf.auth.user");
+ }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/SimpleDBAuthenticator.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/SimpleDBAuthenticator.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/SimpleDBAuthenticator.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/SimpleDBAuthenticator.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web.auth;
+
+import java.util.Map;
+import javax.servlet.ServletContext;
+import org.apache.velocity.tools.view.context.ViewContext;
+import org.apache.velocity.velosurf.context.DBReference;
+import org.apache.velocity.velosurf.context.Instance;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.web.VelosurfTool;
+import org.apache.velocity.velosurf.web.auth.BaseAuthenticator;
+
+/**
+ * <p>Authenticator basic implementation.</p>
+ * <p>It accepts the four following parameters in <code>toolbox.xml</code>:</p>
+ * <ul>
+ * <li><code>method</code> (inherited from <code>BaseAuthenticator</code>) the encryption method to use (default to none,
+ * an example client-side javascript encryption is provided for the method HmacMD5).</li>
+ * <li><code>user-by-login</code> name of the Velosurf root attribute that returns a user given its login.</li>
+ * <li><code>login-parameter</code> name of the external parameter 'login' in the previous attribute.</li>
+ * <li><code>password-field</code> name of the password field.</li>
+ * </ul>
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class SimpleDBAuthenticator extends BaseAuthenticator
+{
+ /** database. */
+ protected DBReference db = null;
+
+ /** key used in toolbox.xml to indicate the "user by login" root attribute. */
+ private static final String USER_BY_LOGIN_KEY = "user-by-login";
+
+ /** key used in toolbox.xml to indicate the name of the login parameter in the "user by login" attribute. */
+ private static final String LOGIN_PARAMETER_KEY = "login-parameter";
+
+ /** key used in toolbox.xml to indicate the name of the password field in the "user by login" attribute. */
+ private static final String PASSWORD_FIELD_KEY = "password-field";
+
+ /** default name of the "user by login" root attribute. */
+ private static final String USER_BY_LOGIN_DEFAULT = "user_by_login";
+
+ /** default name for the "login" parameter. */
+ private static final String LOGIN_PARAMETER_DEFAULT = "login";
+
+ /** default name of the "password" field. */
+ private static final String PASSWORD_FIELD_DEFAULT = "password";
+
+ /** configuration. */
+ private Map config = null;
+
+ /** "user by login" root attribute name. */
+ private String userByLogin = USER_BY_LOGIN_DEFAULT;
+
+ /** login parameter name */
+ private String loginParameter = LOGIN_PARAMETER_DEFAULT;
+
+ /** password field name */
+ private String passwordField = PASSWORD_FIELD_DEFAULT;
+
+ /**
+ * initialize this tool.
+ * @param initData a view context
+ */
+ public void init(Object initData)
+ {
+ super.init(initData);
+
+ // init only if there was no error in super class
+ if(initData instanceof ViewContext)
+ {
+ if(db == null)
+ {
+ initDB(((ViewContext)initData).getServletContext());
+ }
+ }
+ if(config != null)
+ {
+ String value;
+
+ value = (String)config.get(USER_BY_LOGIN_KEY);
+ if(value != null)
+ {
+ userByLogin = value;
+ }
+ value = (String)config.get(PASSWORD_FIELD_KEY);
+ if(value != null)
+ {
+ passwordField = value;
+ }
+ value = (String)config.get(LOGIN_PARAMETER_KEY);
+ if(value != null)
+ {
+ loginParameter = value;
+ }
+ }
+ }
+
+ protected void initDB(ServletContext ctx)
+ {
+ db = VelosurfTool.getDefaultInstance(ctx);
+ }
+
+ /**
+ * externally set the db reference
+ * @param db DBReference
+ */
+ public void setDBReference(DBReference db)
+ {
+ this.db = db;
+ }
+
+ /**
+ * get the password for this login.
+ * @param login login
+ * @return password or null
+ */
+ public String getPassword(String login)
+ {
+ Map user = null;
+
+ synchronized(db)
+ {
+ db.put(loginParameter, login);
+ user = (Map)db.get(userByLogin);
+ }
+ if(user != null)
+ {
+ return(String)user.get(passwordField);
+ }
+ return null;
+ }
+
+ /**
+ * get the user object for this login.
+ * @param login login
+ * @return user object
+ */
+ public Object getUser(String login)
+ {
+ synchronized(db)
+ {
+ db.put(loginParameter, login);
+ return db.get(userByLogin);
+ }
+ }
+
+ /**
+ * configure this tool.
+ * @param map
+ */
+ public void configure(Map map)
+ {
+ super.configure(map);
+ config = map;
+ }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/package.html
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/package.html?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/package.html (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/auth/package.html Fri Mar 9 16:23:25 2012
@@ -0,0 +1,23 @@
+<html>
+<body bgcolor="white">
+
+Contains classes related to authentication.
+
+<h2>Package Specification</h2>
+
+<!-- ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT GO THERE
+<ul>
+ <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+ <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/l10n/HTTPLocalizerTool.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/l10n/HTTPLocalizerTool.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/l10n/HTTPLocalizerTool.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/web/l10n/HTTPLocalizerTool.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.web.l10n;
+
+import java.lang.ref.WeakReference;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import org.apache.velocity.tools.view.context.ViewContext;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>This class rely on the "Accepted-Language" HTTP header to detect
+ * the appropriate locale to be used.</p>
+ *
+ * <p>This tool accepts a "default-locale" configuration parameter in toolbox.xml.</p>
+ * <p>It is meant for the session scope.</p>
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public abstract class HTTPLocalizerTool implements Localizer
+{
+ /**
+ * Initialize this tool.
+ * @param initData a view context
+ */
+ public void init(Object initData)
+ {
+ if(initData instanceof ViewContext)
+ {
+ HttpSession session = ((ViewContext)initData).getRequest().getSession(true);
+
+ if(session != null)
+ {
+ this.session = new WeakReference<HttpSession>(session);
+
+ Locale locale = (Locale)session.getAttribute("velosurf.l10n.active-locale");
+
+ if(locale == null)
+ {
+ /* means the localization filter did not intercept this query */
+ locale = getBestLocale(listFromEnum(((ViewContext)initData).getRequest().getLocales()));
+ Logger.trace("l10n: unlocalized page - using locale " + locale);
+ }
+ setLocale(locale);
+ }
+ }
+ else
+ {
+ Logger.error("l10n: Localizer tool should be used in a session scope!");
+ return;
+ }
+ }
+
+ /**
+ * Transform an enumeration into a list of locales.
+ * @param e enumeration
+ * @return a list of locales
+ */
+ private static List<Locale> listFromEnum(Enumeration e)
+ {
+ List<Locale> list = new ArrayList<Locale>();
+
+ while(e.hasMoreElements())
+ {
+ list.add((Locale)e.nextElement());
+ }
+ return list;
+ }
+
+ /**
+ * Get the locale best matching available localized data among a list.
+ * @param locales list of input locales
+ * @return best matching locale
+ */
+ public Locale getBestLocale(List<Locale> locales)
+ {
+ for(Locale locale : locales)
+ {
+ if(hasLocale(locale))
+ {
+ return locale;
+ }
+ }
+
+ /* second pass without the country code */
+ for(Locale locale : locales)
+ {
+ String country = locale.getCountry();
+
+ if(country != null && country.length() > 0)
+ {
+ Locale l = new Locale(locale.getLanguage());
+
+ if(hasLocale(l))
+ {
+ return l;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check for the presence of a locale.
+ * @param locale locale to check
+ * @return true if present
+ */
+ public abstract boolean hasLocale(Locale locale);
+
+ /**
+ * Current locale setter.
+ * @param locale locale
+ */
+ public void setLocale(Locale locale)
+ {
+ this.locale = locale;
+ }
+
+ /**
+ * Current lcoale getter.
+ *
+ * @return current locale
+ */
+ public Locale getLocale()
+ {
+ checkLocaleChange();
+ return locale;
+ }
+
+ /**
+ * Check that the locale has not changed in the session.
+ */
+ public void checkLocaleChange()
+ {
+ if(session == null) return;
+ HttpSession s = session.get();
+
+ if(s != null)
+ {
+ Locale l = (Locale)s.getAttribute("velosurf.l10n.active-locale");
+
+ if(l != null &&!l.equals(locale))
+ {
+ setLocale(l);
+ }
+ }
+ }
+
+ /**
+ * Get the localized message for this key.
+ * @param id message key
+ * @return localized message (or id if not found).
+ */
+ public abstract String get(Object id);
+
+ /**
+ * Get the localized parameterized message for this key.
+ * @param id message key
+ * @param params message parameters
+ * @return localized message (or id if not found).
+ */
+ public String get(Object id, Object... params)
+ {
+ String message = get(id).replaceAll("'", "''");
+
+ return MessageFormat.format(message, params);
+ }
+
+ /** keep a reference on the session */
+ private WeakReference<HttpSession> session = null;
+
+ /** current locale */
+ protected Locale locale = null;
+}