From 64473431f7bb684a2d05b6f8a15710df59e2e426 Mon Sep 17 00:00:00 2001
From: Endi Sukma Dewata <edewata@redhat.com>
Date: Mon, 30 Jul 2012 19:05:37 -0500
Subject: [PATCH] Added SSL authenticator with fallback.

A custom Tomcat authenticator has been added to authenticate users
using client certificate if provided, otherwise it will fallback to
BASIC/FORM authentication.

Ticket #107
---
 .../realm/SSLAuthenticatorWithFallback.java        | 169 +++++++++++++++++++++
 1 file changed, 169 insertions(+)
 create mode 100644 base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java

diff --git a/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java b/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c8e2c28e9c79d734136916f26e1d83273bb1753
--- /dev/null
+++ b/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java
@@ -0,0 +1,169 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2012 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmscore.realm;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.BasicAuthenticator;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.authenticator.SSLAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.deploy.LoginConfig;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
+
+    public final static String BASIC_AUTHENTICATOR = "BASIC";
+    public final static String FORM_AUTHENTICATOR = "FORM";
+
+    String fallbackMethod = BASIC_AUTHENTICATOR;
+
+    AuthenticatorBase sslAuthenticator = new SSLAuthenticator();
+    AuthenticatorBase fallbackAuthenticator = new BasicAuthenticator();
+
+    public SSLAuthenticatorWithFallback() {
+        log("Creating SSL authenticator with fallback");
+    }
+
+    @Override
+    public String getInfo() {
+        return "SSL authenticator with "+fallbackMethod+" fallback.";
+    }
+
+    public String getFallbackMethod() {
+        return fallbackMethod;
+    }
+
+    public void setFallbackMethod(String fallbackMethod) {
+        log("Fallback method: "+fallbackMethod);
+        this.fallbackMethod = fallbackMethod;
+
+        if (BASIC_AUTHENTICATOR.equalsIgnoreCase(fallbackMethod)) {
+            fallbackAuthenticator = new BasicAuthenticator();
+
+        } else if (FORM_AUTHENTICATOR.equalsIgnoreCase(fallbackMethod)) {
+            fallbackAuthenticator = new FormAuthenticator();
+        }
+
+    }
+
+    @Override
+    public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
+
+        log("Authenticating client");
+
+        X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
+        boolean result;
+
+        if (certs != null && certs.length > 0) {
+            log("Authenticate with client certificate authentication");
+            HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
+                public void setHeader(String name, String value) {
+                    log("SSL auth header: "+name+"="+value);
+                };
+                public void sendError(int code) {
+                    log("SSL auth return code: "+code);
+                }
+            };
+            result = sslAuthenticator.authenticate(request, wrapper, config);
+
+        } else {
+            log("Authenticating with "+fallbackMethod+" authenication");
+            HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
+                public void setHeader(String name, String value) {
+                    log("Fallback auth header: "+name+"="+value);
+                };
+                public void sendError(int code) {
+                    log("Fallback auth return code: "+code);
+                }
+            };
+            result = fallbackAuthenticator.authenticate(request, wrapper, config);
+        }
+
+        if (result)
+            return true;
+
+        log("Result: "+result);
+
+        StringBuilder value = new StringBuilder(16);
+        value.append("Basic realm=\"");
+        if (config.getRealmName() == null) {
+            value.append(REALM_NAME);
+        } else {
+            value.append(config.getRealmName());
+        }
+        value.append('\"');
+        response.setHeader(AUTH_HEADER_NAME, value.toString());
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+
+        return false;
+    }
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.CLIENT_CERT_AUTH;
+    };
+
+    @Override
+    public void setContainer(Container container) {
+        log("Setting container");
+        super.setContainer(container);
+        sslAuthenticator.setContainer(container);
+        fallbackAuthenticator.setContainer(container);
+    }
+
+    @Override
+    protected void initInternal() throws LifecycleException {
+        log("Initializing authenticators");
+        super.initInternal();
+        sslAuthenticator.init();
+        fallbackAuthenticator.init();
+    }
+
+    @Override
+    public void startInternal() throws LifecycleException {
+        log("Starting authenticators");
+        super.startInternal();
+        sslAuthenticator.start();
+        fallbackAuthenticator.start();
+    }
+
+    @Override
+    public void stopInternal() throws LifecycleException {
+        log("Stopping authenticators");
+        super.stopInternal();
+        sslAuthenticator.stop();
+        fallbackAuthenticator.stop();
+    }
+
+    public void log(String message) {
+        System.out.println("SSLAuthenticatorWithFallback: "+message);
+    }
+}
-- 
1.7.11.2

