summaryrefslogtreecommitdiff
path: root/java/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com')
-rw-r--r--java/src/com/memberwebs/httpauth/BaseHttpAuthenticator.java233
-rw-r--r--java/src/com/memberwebs/httpauth/HttpAuthConnection.java347
-rw-r--r--java/src/com/memberwebs/httpauth/HttpAuthConnectionPool.java172
-rw-r--r--java/src/com/memberwebs/httpauth/HttpAuthConnectionSource.java61
-rw-r--r--java/src/com/memberwebs/httpauth/HttpAuthException.java139
-rw-r--r--java/src/com/memberwebs/httpauth/HttpAuthFilter.java57
-rw-r--r--java/src/com/memberwebs/httpauth/jetty/JettyHttpAuthenticator.java195
-rw-r--r--java/src/com/memberwebs/httpauth/servlet/ServletHttpAuthenticator.java156
8 files changed, 1360 insertions, 0 deletions
diff --git a/java/src/com/memberwebs/httpauth/BaseHttpAuthenticator.java b/java/src/com/memberwebs/httpauth/BaseHttpAuthenticator.java
new file mode 100644
index 0000000..8dd15f1
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/BaseHttpAuthenticator.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+/**
+ * Base class for HttpAuth authenticators. This class in not generally used
+ * outside of the HttpAuth code.
+ *
+ * @author Nate Nielsen
+ */
+public abstract class BaseHttpAuthenticator
+{
+ protected HttpAuthConnectionSource m_source;
+
+ protected BaseHttpAuthenticator(HttpAuthConnectionSource source)
+ {
+ m_source = source;
+ }
+
+ protected abstract void addResponseHeader(Object response, String name, String value);
+ protected abstract void setResponseResult(Object response, int code)
+ throws IOException;
+ protected abstract Enumeration getHeaderValues(Object request, String name);
+ protected abstract Enumeration getHeaderNames(Object request);
+
+ protected String authenticateRequest(Object request, Object response,
+ String connid, String method,
+ String uri, String[] authtypes)
+ throws HttpAuthException
+ {
+ // Prepare and send the request line
+ String args = connid + " " + method + " " + uri;
+ HttpAuthConnection conn = null;
+ HttpAuthConnection.Response rsp = null;
+
+ try
+ {
+ // Retry loop
+ for(;;)
+ {
+ conn = m_source.getOpenConnection();
+
+ try
+ {
+ conn.sendLine("AUTH " + args);
+ sendHeaders(conn, request, authtypes);
+
+ // If above was successful get out of this retry loop
+ break;
+ }
+ catch(SocketException e)
+ {
+ // Always close connections that have errors on them
+ conn.close();
+
+ // Stupid hack but java doesn't let us do any better just now
+ if(!e.getMessage().startsWith("Broken pipe"))
+ throw new HttpAuthException("error communicating with httpauthd", e);
+ }
+
+ // The following code only gets executed when our sockets
+ // have been disconnected. We loop up and try again. We're
+ // pretty safe in doing this because the act of opening a
+ // HttpAuthConnection sends some data through it, so only
+ // stale connections should end up here.
+ m_source.doneWithConnection(conn);
+ }
+
+ rsp = conn.readResponse(200);
+ copyHeaders(conn, response, authtypes);
+
+ if(rsp.ccode != 200)
+ {
+ setResponseResult(response, rsp.ccode);
+ return null;
+ }
+ else
+ {
+ return rsp.details;
+ }
+ }
+ catch(IOException e)
+ {
+ try
+ {
+ // Always close connections that have errors on them
+ conn.close();
+ }
+ catch(IOException ex)
+ { }
+
+ throw new HttpAuthException("error communicating with httpauthd", e);
+ }
+ finally
+ {
+ if(conn != null)
+ m_source.doneWithConnection(conn);
+ }
+ }
+
+ protected void copyHeaders(HttpAuthConnection conn, Object response,
+ String[] authtypes)
+ throws IOException
+ {
+ for(;;)
+ {
+ String line = conn.readLine();
+ if(line != null)
+ {
+ line.trim();
+ if(line.length() == 0)
+ line = null;
+ }
+
+ if(line == null)
+ break;
+
+ int pos = line.indexOf(":");
+
+ // Skip invalid lines
+ if(pos == -1)
+ continue;
+
+ String name = line.substring(0, pos).trim();
+ String value = line.substring(pos + 1).trim();
+
+ // Drop junk that's not allowed
+ if(authtypes != null && authtypes.length > 0)
+ {
+ if(name.equalsIgnoreCase("WWW-Authenticate"))
+ {
+ boolean ok = false;
+
+ for(int i = 0; i < authtypes.length; i++)
+ {
+ if(value.startsWith(authtypes[i]))
+ {
+ ok = true;
+ break;
+ }
+ }
+
+ if(!ok)
+ continue;
+ }
+ }
+
+ addResponseHeader(response, name, value);
+ }
+ }
+
+ /**
+ * @param req
+ */
+ protected void sendHeaders(HttpAuthConnection conn, Object request,
+ String[] authtypes)
+ throws IOException
+ {
+ Enumeration e = getHeaderNames(request);
+
+ while(e.hasMoreElements())
+ {
+ String name = (String)e.nextElement();
+ if(!name.equalsIgnoreCase("Authorization"))
+ continue;
+
+ Enumeration e2 = getHeaderValues(request, name);
+ while(e2.hasMoreElements())
+ {
+ String value = (String)e2.nextElement();
+
+ if(authtypes == null || authtypes.length == 0)
+ {
+ conn.sendLine(name + ": " + value);
+ }
+ else
+ {
+ for(int i = 0; i < authtypes.length; i++)
+ {
+ if(value.startsWith(authtypes[i]))
+ {
+ conn.sendLine(name + ": " + value);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Headers always end in a blank line
+ conn.sendLine("");
+ }
+}
diff --git a/java/src/com/memberwebs/httpauth/HttpAuthConnection.java b/java/src/com/memberwebs/httpauth/HttpAuthConnection.java
new file mode 100644
index 0000000..7604d31
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/HttpAuthConnection.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A connection to the HttpAuth daemon.
+ *
+ * @author Nate Nielsen
+ */
+public class HttpAuthConnection
+ implements HttpAuthConnectionSource
+{
+ /**
+ * The option to set the authenticaton handler.
+ */
+ public static final String OPT_HANDLER = "Handler";
+
+ /**
+ * The option to set the digest domains.
+ */
+ public static final String OPT_DOMAIN = "Domain";
+
+ private Writer m_output;
+ private BufferedReader m_input;
+ private Socket m_socket;
+ private boolean m_checkedout;
+
+ /**
+ * Connects to the HttpAuth daemon at the given address and port. The
+ * appropriate handler is set.
+ *
+ * @param hostname The address where the HttpAuth daemon is running.
+ * @param port The TCP port for the HttpAuth daemon.
+ * @param handler The HttpAuth handler to use.
+ */
+ public void open(String hostname, int port, String handler)
+ throws IOException, HttpAuthException
+ {
+ HashMap options = new HashMap();
+ options.put(OPT_HANDLER, handler);
+ open(hostname, port, options);
+ }
+
+ /**
+ * Connects to the HttpAuth daemon at the given address and port. Various
+ * options (including the handler) can be set.
+ *
+ * @param hostname The address where the HttpAuth daemon is running.
+ * @param port The TCP port for the HttpAuth daemon.
+ * @param options The HttpAuth options to use. This must include {@link #OPT_HANDLER}.
+ */
+ public void open(String hostname, int port, Map options)
+ throws IOException, HttpAuthException
+ {
+ synchronized(this)
+ {
+ close();
+
+ m_socket = new Socket();
+ m_socket.connect(new InetSocketAddress(hostname, port), 60000);
+
+ m_input = new BufferedReader(new InputStreamReader(m_socket.getInputStream()));
+ m_output = new OutputStreamWriter(m_socket.getOutputStream());
+
+ // Okay now read the version number
+ Response ver = readResponse(100);
+ if(!ver.details.trim().equals("HTTPAUTH/1.0"))
+ throw new HttpAuthException("httpauthd speaking wrong version of protocol: " + ver.details);
+
+ // Set any options as necessary
+ Iterator it = options.keySet().iterator();
+ while(it.hasNext())
+ {
+ String name = (String)it.next();
+ String value = (String)options.get(name);
+
+ // Set the options request
+ sendLine("SET " + name + " " + value);
+
+ // This throws an exception if we don't get the right response
+ readResponse(202);
+ }
+ }
+ }
+
+ /**
+ * Closes the connection to the HttpAuth daemon.
+ */
+ public void close()
+ throws IOException
+ {
+ synchronized(this)
+ {
+ if(m_socket != null && m_output != null)
+ {
+ try
+ {
+ sendLine("QUIT");
+ }
+ catch(IOException e)
+ {
+ }
+ }
+
+ if(m_output != null)
+ {
+ m_output.close();
+ m_output = null;
+ }
+
+ if(m_input != null)
+ {
+ m_input.close();
+ m_input = null;
+ }
+
+ if(m_socket != null)
+ {
+ m_socket.close();
+ m_socket = null;
+ }
+ }
+ }
+
+ /**
+ * Checks whether we're connected to the HttpAuth daemon.
+ */
+ public boolean isConnected()
+ {
+ return m_socket != null && m_socket.isConnected()
+ && m_input != null && m_output != null;
+ }
+
+ /**
+ * Sends a command string to the HttpAuth daemon.
+ *
+ * @param string The command to send.
+ */
+ public void sendLine(String string)
+ throws IOException
+ {
+ synchronized(this)
+ {
+ m_output.write(string + "\n");
+ m_output.flush();
+ }
+ }
+
+ /**
+ * Reads a response line from the HttpAuth daemon.
+ *
+ * @return The response line.
+ */
+ public String readLine()
+ throws IOException
+ {
+ synchronized(this)
+ {
+ return m_input.readLine();
+ }
+ }
+
+ /* ---------------------------------------------------------------------------
+ * HttpAuthConnectionSource
+ *
+ * This is a simple implementation which allows a single connection
+ * to be used in leu of a connection pool.
+ */
+
+ /**
+ * Treats this single connection as a pool. Checks out the current
+ * connection. Use {@link HttpAuthConnectionPool} instead.
+ * @return The connection.
+ */
+ public HttpAuthConnection getOpenConnection()
+ throws HttpAuthException
+ {
+ synchronized(this)
+ {
+ if(isConnected() && !m_checkedout)
+ return null;
+
+ m_checkedout = true;
+ return this;
+ }
+ }
+
+ /**
+ * Treats this single connection as a pool. Checks in the current
+ * connection. Use {@link HttpAuthConnectionPool} instead.
+ * @param conn The connection.
+ */
+ public void doneWithConnection(HttpAuthConnection conn)
+ throws HttpAuthException
+ {
+ synchronized(this)
+ {
+ m_checkedout = false;
+ }
+ }
+
+ // -----------------------------------------------------------------------------
+ // Helper functions
+
+ protected Response readResponse(int expected)
+ throws HttpAuthException, IOException
+ {
+ Response resp = new Response();
+ String line = readLine();
+
+ if(line == null)
+ throw new IOException("unexpected end of data from httpauthd");
+
+ String word = parseWord(line);
+ line = line.substring(word.length());
+ word = word.trim();
+
+ try
+ {
+ int code = Integer.parseInt(word, 10);
+ if(code < 100 || code > 599)
+ throw new NumberFormatException();
+
+ resp.code = code;
+ }
+ catch(NumberFormatException e)
+ {
+ throw new HttpAuthException("httpauth protocol error. invalid code received: " + word);
+ }
+
+ if(expected != 0 && resp.code != expected)
+ throw new HttpAuthException("httpauth protocol error. expected code '" + expected + "' and got: " + resp.code);
+
+ if(resp.code == 200)
+ {
+ word = parseWord(line);
+ line = line.substring(word.length()).trim();
+ word = word.trim();
+
+ try
+ {
+ int code = Integer.parseInt(word, 10);
+ if(code < 100 || code > 599)
+ throw new NumberFormatException();
+
+ resp.ccode = code;
+ }
+ catch(NumberFormatException e)
+ {
+ throw new HttpAuthException("httpauth protocol error. invalid code received: " + word);
+ }
+ }
+
+ if(line.length() > 0)
+ resp.details = line;
+
+ return resp;
+ }
+
+ protected String parseWord(String line)
+ {
+ int start = -1;
+ int end;
+ char ch;
+
+ while(true)
+ {
+ if(++start >= line.length())
+ return "";
+
+ ch = line.charAt(start);
+ if(ch != ' ' && ch != '\t')
+ break;
+ }
+
+ end = start - 1;
+
+ while(true)
+ {
+ if(++end >= line.length())
+ break;
+
+ ch = line.charAt(end);
+ if(ch == ' ' || ch == '\t')
+ break;
+ }
+
+ if(end == -1)
+ return "";
+
+ return line.substring(0, end);
+ }
+
+ class Response
+ {
+ int code;
+ int ccode;
+ String details;
+ };
+}
diff --git a/java/src/com/memberwebs/httpauth/HttpAuthConnectionPool.java b/java/src/com/memberwebs/httpauth/HttpAuthConnectionPool.java
new file mode 100644
index 0000000..089402b
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/HttpAuthConnectionPool.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * A pool of connections to the HttpAuth daemon.
+ *
+ * @author Nate Nielsen
+ */
+public class HttpAuthConnectionPool
+ implements HttpAuthConnectionSource
+{
+ private Stack m_connections;
+ private short m_checkedout;
+ private short m_max;
+
+ private String m_hostname;
+ private int m_port;
+ private Map m_options;
+
+ /**
+ * Creates a new pool of connections to the HttpAuth daemon at the given
+ * address. The connections will be setup to use the given handler.
+ *
+ * @param hostname The address at which the HttpAuth daemon is running.
+ * @param port The TCP port at which the HttpAuth daemon is listening on.
+ * @param handler The HttpAuth handler to use.
+ */
+ public HttpAuthConnectionPool(String hostname, int port, String handler)
+ {
+ m_hostname = hostname;
+ m_port = port;
+ m_checkedout = 0;
+ m_max = 16;
+ m_connections = new Stack();
+ m_options = new HashMap();
+ m_options.put(HttpAuthConnection.OPT_HANDLER, handler);
+ }
+
+ /**
+ * Creates a new pool of connections to the HttpAuth daemon at the given
+ * address. The connections will be setup with the various options.
+ *
+ * @param hostname The address at which the HttpAuth daemon is running.
+ * @param port The TCP port at which the HttpAuth daemon is listening on.
+ * @param options The HttpAuth options to use. This must include {@link #HttpAuthConnection.OPT_HANDLER}.
+ */
+ public HttpAuthConnectionPool(String hostname, int port, Map options)
+ {
+ m_hostname = hostname;
+ m_port = port;
+ m_checkedout = 0;
+ m_max = 16;
+ m_connections = new Stack();
+ m_options = options;
+ }
+
+ /**
+ * Set the maximum number of connections available in this pool.
+ * @param max The maximum number of connections.
+ */
+ public void setMax(short max)
+ {
+ m_max = max;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * HttpAuthConnectionSource
+ */
+
+ /**
+ * Retrieves a connection from the pool.
+ * @return The connection.
+ */
+ public HttpAuthConnection getOpenConnection()
+ throws HttpAuthException
+ {
+ synchronized(this)
+ {
+ HttpAuthConnection conn = null;
+
+ while(!m_connections.empty())
+ {
+ conn = (HttpAuthConnection)m_connections.pop();
+ if(conn != null && conn.isConnected())
+ {
+ m_checkedout++;
+ return conn;
+ }
+ }
+
+ if(m_checkedout >= m_max)
+ throw new HttpAuthException("too many connections to httpauthd: " + m_checkedout);
+
+ try
+ {
+ // Otherwise we make a new connection
+ conn = new HttpAuthConnection();
+ conn.open(m_hostname, m_port, m_options);
+ m_checkedout++;
+ return conn;
+ }
+ catch(IOException e)
+ {
+ throw new HttpAuthException("couldn't connect to httpauthd: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Puts a connection back in the pool.
+ * @param conn The connection.
+ */
+ public void doneWithConnection(HttpAuthConnection conn)
+ throws HttpAuthException
+ {
+ synchronized(this)
+ {
+ if(m_checkedout > 0)
+ m_checkedout--;
+
+ if(conn.isConnected())
+ {
+ // TODO: I don't know of any way to drain the remaining
+ // junk out of a socket here without blocking. :(
+
+ m_connections.push(conn);
+ }
+ }
+ }
+}
diff --git a/java/src/com/memberwebs/httpauth/HttpAuthConnectionSource.java b/java/src/com/memberwebs/httpauth/HttpAuthConnectionSource.java
new file mode 100644
index 0000000..eab8eb5
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/HttpAuthConnectionSource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+/**
+ * An interface for a pool of HttpAuth connections.
+ *
+ * @author Nate Nielsen
+ */
+public interface HttpAuthConnectionSource
+{
+ /**
+ * Retrieves a connection from the pool.
+ * @return The connection.
+ */
+ public HttpAuthConnection getOpenConnection()
+ throws HttpAuthException;
+
+ /**
+ * Puts a connection back in the pool.
+ * @param conn The connection.
+ */
+ public void doneWithConnection(HttpAuthConnection conn)
+ throws HttpAuthException;
+}
diff --git a/java/src/com/memberwebs/httpauth/HttpAuthException.java b/java/src/com/memberwebs/httpauth/HttpAuthException.java
new file mode 100644
index 0000000..c342302
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/HttpAuthException.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+
+/**
+ * A general utility exception which can wrap another exception or
+ * hold it's own message
+ *
+ * @author Nate Nielsen
+ */
+public class HttpAuthException
+ extends Exception
+{
+ // The internal exception
+ private Exception m_exception;
+
+ /**
+ * Creates a new HttpAuthException with a message
+ *
+ * @param message The message to include.
+ */
+ public HttpAuthException(String message)
+ {
+ this(message, null);
+ }
+
+ /**
+ * Creates a new HttpAuthException with an exception
+ * held internally.
+ *
+ * @param e The internal exception.
+ */
+ public HttpAuthException(Exception e)
+ {
+ this(null, e);
+ }
+
+ /**
+ * Creates a new HttpAuthException with a message and an
+ * exception exception held internally.
+ *
+ * @param message The message to include.
+ * @param e The internal exception.
+ */
+ public HttpAuthException(String message, Exception e)
+ {
+ super(message);
+ if(e != null)
+ m_exception = e;
+ }
+
+ /**
+ * Returns the error message of this exception if present, or
+ * the internal exception.
+ *
+ * @return The message.
+ */
+ public String getMessage()
+ {
+ String message = super.getMessage();
+
+ if(message == null && m_exception != null)
+ message = m_exception.getMessage();
+
+ return message;
+ }
+
+ /**
+ * Returns the internal exception, or this if not present.
+ *
+ * @return The exception.
+ */
+ public Exception getException()
+ {
+ if(m_exception == null)
+ return this;
+ else
+ return m_exception;
+ }
+
+ /**
+ * Converts this object to a string.
+ *
+ * @return The string value.
+ */
+ public String toString ()
+ {
+ if(m_exception != null)
+ return m_exception.toString();
+ else
+ return super.toString();
+ }
+
+ public StackTraceElement[] getStackTrace()
+ {
+ return getStackTrace();
+ }
+
+ // A dumb id to suppress compiler warnings
+ private static final long serialVersionUID = 9117749574169057520L;
+
+}
diff --git a/java/src/com/memberwebs/httpauth/HttpAuthFilter.java b/java/src/com/memberwebs/httpauth/HttpAuthFilter.java
new file mode 100644
index 0000000..1c7367d
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/HttpAuthFilter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth;
+
+/**
+ * Implement this interface if you want to limit the type of
+ * authentication available for each request. This could allow
+ * more insecure authentication over SSL for example.
+ *
+ * @author Nate Nielsen
+ */
+public interface HttpAuthFilter
+{
+ /**
+ * Return the types of authentication allowed.
+ * @param address The address for the request.
+ * @param secure Whether the request came over a secure channel or not.
+ * @return The types of authentication allowed.
+ */
+ String[] getAuthTypes(String address, boolean secure);
+}
diff --git a/java/src/com/memberwebs/httpauth/jetty/JettyHttpAuthenticator.java b/java/src/com/memberwebs/httpauth/jetty/JettyHttpAuthenticator.java
new file mode 100644
index 0000000..bcca142
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/jetty/JettyHttpAuthenticator.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth.jetty;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.mortbay.http.HttpRequest;
+import org.mortbay.http.HttpResponse;
+import org.mortbay.http.UserPrincipal;
+import org.mortbay.http.UserRealm;
+import org.mortbay.http.SecurityConstraint.Authenticator;
+
+import com.memberwebs.httpauth.BaseHttpAuthenticator;
+import com.memberwebs.httpauth.HttpAuthConnectionSource;
+import com.memberwebs.httpauth.HttpAuthException;
+import com.memberwebs.httpauth.HttpAuthFilter;
+
+/**
+ * An authenticator for the Jetty web server. An instance of this object
+ * is used by a Jetty HttpContext to perform authentication.
+ *
+ * @author Nate Nielsen
+ */
+public class JettyHttpAuthenticator
+ extends BaseHttpAuthenticator
+ implements Authenticator
+{
+ protected HttpAuthFilter m_filter;
+
+ /**
+ * Creates a new JettyHttpAuthenticator.
+ *
+ * @param source A source of HttpAuth connections
+ * (such as {@link #HttpAuthConnectionPool})
+ * @param filter An optional filter which determines
+ * which types of authentication may be performed.
+ */
+ public JettyHttpAuthenticator(HttpAuthConnectionSource source,
+ HttpAuthFilter filter)
+ {
+ super(source);
+ m_filter = filter;
+ }
+
+ /**
+ * Called by Jetty's HttpContext to perform authentication.
+ * @param realm
+ * @param pathInContext
+ * @param request
+ * @param response
+ * @return Used by Jetty to identify the user.
+ */
+ public UserPrincipal authenticated(UserRealm realm, String pathInContext,
+ HttpRequest request, HttpResponse response)
+ throws IOException
+ {
+ // We ignore the realm
+
+ String[] authtypes = null;
+
+ if(m_filter != null)
+ authtypes = m_filter.getAuthTypes(request.getRemoteAddr(), request.isConfidential());
+
+ String user = null;
+
+ try
+ {
+ user = authenticateRequest(request, response, "XXX", request.getMethod(),
+ request.getURI().toString(), authtypes);
+ }
+ catch(HttpAuthException e)
+ {
+ throw new IOException(e.getMessage());
+ }
+
+ if(user == null)
+ return null;
+
+ HttpAuthPrincipal principal = new HttpAuthPrincipal(user);
+
+ request.setAuthType("HTTPAUTH");
+ request.setAuthUser(user);
+ request.setUserPrincipal(principal);
+
+ return principal;
+ }
+
+ /**
+ * Called by Jetty's HttpContext to get authentication method.
+ * @return Used by Jetty to identify the method.
+ */
+ public String getAuthMethod()
+ {
+ return "HTTPAUTH";
+ }
+
+ /* -------------------------------------------------------------------------
+ * OVERRIDES
+ */
+
+ protected void addResponseHeader(Object response, String name, String value)
+ {
+ HttpResponse resp = (HttpResponse)response;
+ resp.addField(name, value);
+ }
+
+ protected void setResponseResult(Object response, int code)
+ throws IOException
+ {
+ HttpResponse resp = (HttpResponse)response;
+
+ if(code >= 300)
+ resp.sendError(code);
+ else
+ resp.setStatus(code);
+ }
+
+ protected Enumeration getHeaderValues(Object request, String name)
+ {
+ HttpRequest req = (HttpRequest)request;
+ return req.getFieldValues(name);
+ }
+
+ protected Enumeration getHeaderNames(Object request)
+ {
+ HttpRequest req = (HttpRequest)request;
+ return req.getFieldNames();
+ }
+
+ protected class HttpAuthPrincipal
+ implements UserPrincipal
+ {
+ private String m_name;
+
+ public HttpAuthPrincipal(String name)
+ {
+ m_name = name;
+ }
+
+ public boolean isAuthenticated()
+ {
+ return true;
+ }
+
+ public boolean isUserInRole(String role)
+ {
+ return false;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+ }
+
+ // A dumb ID to suppress compiler warnings
+ private static final long serialVersionUID = -1266258707017895782L;
+}
diff --git a/java/src/com/memberwebs/httpauth/servlet/ServletHttpAuthenticator.java b/java/src/com/memberwebs/httpauth/servlet/ServletHttpAuthenticator.java
new file mode 100644
index 0000000..4e2d025
--- /dev/null
+++ b/java/src/com/memberwebs/httpauth/servlet/ServletHttpAuthenticator.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+package com.memberwebs.httpauth.servlet;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.memberwebs.httpauth.BaseHttpAuthenticator;
+import com.memberwebs.httpauth.HttpAuthConnectionSource;
+import com.memberwebs.httpauth.HttpAuthException;
+
+/**
+ * A class which can be used by servlets to authenticate the user for
+ * a given request.
+ *
+ * Note that the user will not be set on the request and will not be
+ * available via req.getRemoteUser().
+ *
+ * @author Nate Nielsen
+ */
+
+public class ServletHttpAuthenticator
+ extends BaseHttpAuthenticator
+{
+ /**
+ * Creates a new ServletHttpAuthenticator.
+ *
+ * @param source A source of HttpAuth connections
+ * (such as {@link #HttpAuthConnectionPool})
+ */
+ public ServletHttpAuthenticator(HttpAuthConnectionSource source)
+ {
+ super(source);
+ }
+
+ /**
+ * Authenticate a request. If the authentication fails, headers and
+ * a response will be sent.
+ *
+ * @param req The HTTP request.
+ * @param resp The HTTP response used when authentication fails.
+ * @return The authenticated user name, or null if unsuccessful.
+ */
+ public String authenticate(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException
+ {
+ return authenticate(req, resp, null);
+ }
+
+ /**
+ * Authenticate a request. If the authentication fails, headers and
+ * a response will be sent. The caller can limit the types of
+ * authentication allowed.
+ *
+ * @param req The HTTP request.
+ * @param resp The HTTP response used when authentication fails.
+ * @param authtypes The types of authentication allowed.
+ * @return The authenticated user name, or null if unsuccessful.
+ */
+ public String authenticate(HttpServletRequest req, HttpServletResponse resp,
+ String[] authtypes)
+ throws ServletException
+ {
+ String method = req.getMethod();
+ String uri = req.getRequestURI();
+
+ String qs = req.getQueryString();
+ if(qs != null)
+ uri += qs;
+
+ // NOTE: We can't set the remote user on the request. That's just
+ // the way the interfaces are designed
+
+ try
+ {
+ return authenticateRequest(req, resp, "XXX", method, uri, authtypes);
+ }
+ catch(HttpAuthException e)
+ {
+ throw new UnavailableException(e.getMessage(), 30);
+ }
+ }
+
+ /* -------------------------------------------------------------------------
+ * OVERRIDES
+ */
+
+ protected void addResponseHeader(Object response, String name, String value)
+ {
+ HttpServletResponse resp = (HttpServletResponse)response;
+ resp.addHeader(name, value);
+ }
+
+ protected void setResponseResult(Object response, int code)
+ throws IOException
+ {
+ HttpServletResponse resp = (HttpServletResponse)response;
+ if(code >= 200 && code < 300)
+ resp.setStatus(code);
+ else
+ resp.sendError(code);
+ }
+
+ protected Enumeration getHeaderValues(Object request, String name)
+ {
+ HttpServletRequest req = (HttpServletRequest)request;
+ return req.getHeaders(name);
+ }
+
+ protected Enumeration getHeaderNames(Object request)
+ {
+ HttpServletRequest req = (HttpServletRequest)request;
+ return req.getHeaderNames();
+ }
+}