Skip to content

Commit

Permalink
pass-through authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
jalpedersen committed Feb 21, 2012
1 parent daf007b commit 60ff635
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 37 deletions.
@@ -0,0 +1,79 @@
package com.github.rnewson.couchdb.lucene;

import java.io.IOException;
import java.util.Collection;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;

public class AuthenticatingHttpClient implements HttpClient {
private final HttpClient delegate;
private final Collection<Header> headers;

public AuthenticatingHttpClient(HttpClient delegate, Collection<Header> headers) {
this.delegate = delegate;
this.headers = headers;
}

public HttpParams getParams() {
return delegate.getParams();
}

public ClientConnectionManager getConnectionManager() {
return delegate.getConnectionManager();
}

public HttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException {
return delegate.execute(addHeaders(request));
}

public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException,
ClientProtocolException {
return delegate.execute(addHeaders(request), context);
}

public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException, ClientProtocolException {
return delegate.execute(target, addHeaders(request));
}

public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException,
ClientProtocolException {
return delegate.execute(target, addHeaders(request), context);
}

public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return delegate.execute(addHeaders(request), responseHandler);
}

public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
throws IOException, ClientProtocolException {
return delegate.execute(addHeaders(request), responseHandler, context);
}

public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler)
throws IOException, ClientProtocolException {
return delegate.execute(target, addHeaders(request), responseHandler);
}

public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler,
HttpContext context) throws IOException, ClientProtocolException {
return delegate.execute(target, addHeaders(request), responseHandler, context);
}

public <T extends HttpRequest> T addHeaders(T request) {
for (Header h: headers) {
request.addHeader(h);
}
return request;
}
}
113 changes: 78 additions & 35 deletions src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java
Expand Up @@ -19,10 +19,16 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.configuration.HierarchicalINIConfiguration;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
Expand All @@ -49,6 +55,7 @@
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
Expand Down Expand Up @@ -129,49 +136,85 @@ public void shutdown() {

}

private static DefaultHttpClient instance;
private static HttpClient instance;
private static HttpClient unauthenticatedClient;

private static HierarchicalINIConfiguration INI;

/**
* Returns a client where each request is responsible for authentication
*
* @return
* @throws MalformedURLException
*/
public static synchronized HttpClient getAuthenticatingInstance(HttpServletRequest request) throws MalformedURLException {
if (unauthenticatedClient == null) {
unauthenticatedClient = createInstance(false);
}
final Collection<Header> headers = new LinkedList<Header>();
@SuppressWarnings("unchecked")
final Enumeration<String> headerEnum = request.getHeaderNames();
while (headerEnum.hasMoreElements()) {
final String name = headerEnum.nextElement();
@SuppressWarnings("unchecked")
final Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
headers.add(new BasicHeader(name, values.nextElement()));
}
}
return new AuthenticatingHttpClient(unauthenticatedClient, headers);
}

/**
* Returns a client where the credentials in the configuration file are used for authentication
*
* @return
* @throws MalformedURLException
*/
public static synchronized HttpClient getInstance() throws MalformedURLException {
if (instance == null) {
final HttpParams params = new BasicHttpParams();
// protocol params.
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setUseExpectContinue(params, false);
// connection params.
HttpConnectionParams.setTcpNoDelay(params, true);
HttpConnectionParams.setStaleCheckingEnabled(params, false);
ConnManagerParams.setMaxTotalConnections(params, 1000);
ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(1000));

final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry
.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 5984));
schemeRegistry
.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
final ClientConnectionManager cm = new ShieldedClientConnManager(
new ThreadSafeClientConnManager(params, schemeRegistry));

instance = new DefaultHttpClient(cm, params);

if (INI != null) {
final CredentialsProvider credsProvider = new BasicCredentialsProvider();
final Iterator<?> it = INI.getKeys();
while (it.hasNext()) {
final String key = (String) it.next();
if (!key.startsWith("lucene.") && key.endsWith(".url")) {
final URL url = new URL(INI.getString(key));
if (url.getUserInfo() != null) {
credsProvider.setCredentials(
new AuthScope(url.getHost(), url.getPort()),
new UsernamePasswordCredentials(url.getUserInfo()));
}
instance = createInstance(true);
}
return instance;
}

private static HttpClient createInstance(boolean authenticate) throws MalformedURLException {
final HttpParams params = new BasicHttpParams();
// protocol params.
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setUseExpectContinue(params, false);
// connection params.
HttpConnectionParams.setTcpNoDelay(params, true);
HttpConnectionParams.setStaleCheckingEnabled(params, false);
ConnManagerParams.setMaxTotalConnections(params, 1000);
ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(1000));

final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry
.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 5984));
schemeRegistry
.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
final ClientConnectionManager cm = new ShieldedClientConnManager(
new ThreadSafeClientConnManager(params, schemeRegistry));

final DefaultHttpClient instance = new DefaultHttpClient(cm, params);

if (INI != null) {
final CredentialsProvider credsProvider = new BasicCredentialsProvider();
final Iterator<?> it = INI.getKeys();
while (it.hasNext()) {
final String key = (String) it.next();
if (!key.startsWith("lucene.") && key.endsWith(".url")) {
final URL url = new URL(INI.getString(key));
if (authenticate && url.getUserInfo() != null) {
credsProvider.setCredentials(
new AuthScope(url.getHost(), url.getPort()),
new UsernamePasswordCredentials(url.getUserInfo()));
}
}
instance.setCredentialsProvider(credsProvider);
instance.addRequestInterceptor(new PreemptiveAuthenticationRequestInterceptor(), 0);
}
instance.setCredentialsProvider(credsProvider);
instance.addRequestInterceptor(new PreemptiveAuthenticationRequestInterceptor(), 0);
}
return instance;
}
Expand Down
28 changes: 26 additions & 2 deletions src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java
Expand Up @@ -36,6 +36,7 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -121,7 +122,7 @@ private void cleanup(final HttpServletRequest req,
ServletUtils.sendJsonSuccess(req, resp);
}

private Couch getCouch(final HttpServletRequest req) throws IOException {
private Couch getCouch(final HttpServletRequest req, HttpClient client) throws IOException {
final String sectionName = new PathParts(req).getKey();
final Configuration section = ini.getSection(sectionName);
if (!section.containsKey("url")) {
Expand All @@ -130,6 +131,14 @@ private Couch getCouch(final HttpServletRequest req) throws IOException {
return new Couch(client, section.getString("url"));
}

private Couch getAuthenticatingCouch(final HttpServletRequest req) throws IOException {
return getCouch(req, HttpClientFactory.getAuthenticatingInstance(req));
}

private Couch getCouch(final HttpServletRequest req) throws IOException {
return getCouch(req, client);
}

private synchronized DatabaseIndexer getIndexer(final Database database)
throws IOException, JSONException {
DatabaseIndexer result = indexers.get(database);
Expand Down Expand Up @@ -186,6 +195,9 @@ private void doGetInternal(final HttpServletRequest req, final HttpServletRespon
handleWelcomeReq(req, resp);
return;
case 5:
if ( ! validateDatabaseRequest(req, resp)) {
return;
}
final DatabaseIndexer indexer = getIndexer(req);
if (indexer == null) {
ServletUtils.sendJsonError(req, resp, 500, "error_creating_index");
Expand Down Expand Up @@ -228,7 +240,19 @@ private void doPostInternal(final HttpServletRequest req, final HttpServletRespo
indexer.admin(req, resp);
return;
}
ServletUtils.sendJsonError(req, resp, 400, "bad_request");
ServletUtils.sendJsonError(req, resp, 400, "bad_request");
}

private boolean validateDatabaseRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException {
final Couch couch = getAuthenticatingCouch(req);
final Database db = couch.getDatabase(new PathParts(req).getDatabaseName());
try {
db.getInfo(); //This will throw an HttpResponseException if not authenticated
return true;
} catch (HttpResponseException e) {
ServletUtils.sendJsonError(req, resp, e.getStatusCode(), new JSONObject(e.getMessage()));
}
return false;
}

}

0 comments on commit 60ff635

Please sign in to comment.