[Pki-devel] [PATCH] 0063 Extract common base class for SSLAuthenticatorWithFallback

Fraser Tweedale ftweedal at redhat.com
Thu Jan 21 10:03:32 UTC 2016


On Wed, Jan 20, 2016 at 06:28:21PM -0600, Endi Sukma Dewata wrote:
> On 12/3/2015 12:31 AM, Fraser Tweedale wrote:
> >The attached patch was written as part of work implementing GSS-API
> >authentication.  We actually might not end up using
> >SSLAuthenticatorWithFallback to interpret the authentication data
> >but I think this refactor is worthwhile on its own, so here's the
> >patch.
> >
> >Cheers,
> >Fraser
> 
> Ideally the SSLAuthenticatorWithFallback for Tomcat 7 should not store the
> LoginConfig as an attribute because potentially it can be shared by multiple
> web applications. Since each PKI web application has a separate instance of
> the SSLAuthenticatorWithFallback this is probably not a problem. So the
> patch is ACKed as is.
> 
> But if you want, the code probably can be modified like this:
> 
>   public boolean authenticate(..., config) {
>       HashMap attributes = new HashMap();
>       attributes.put("loginConfig", config);
>       return doAuthenticate(..., attributes);
>   }
> 
>   public boolean doSubAuthenticate(..., attributes) {
>       LoginConfig config = attributes.get("loginConfig");
>       return auth.authenticate(..., loginConfig);
>   }
> 
>   public String goGetRealName(..., attributes) {
>       LoginConfig config = attributes.get("loginConfig");
>       return loginConfig.getRealName();
>   }
> 
> For Tomcat 8 the attributes map can be null.
> 
> It might even be possible to merge the two implementations. Recent Tomcat 7
> contains additional stuff that reduces the differences with Tomcat 8. But we
> will need to make sure the latest Tomcat 7 is available on all platforms
> that we support. This can be done later though.
> 
Since loginConfig is immediately set with the passed LoginConfig on
each call to authenticate(), we can use ThreadLocal[1] to prevent
interference in a less intrusive way that is still type-safe.

[1] http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html

Updated patch attached.

Thanks for reviewing,
Fraser
-------------- next part --------------
From 67ac39227e5db83c7a4a7ad72364f3dcd30db05e Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Thu, 3 Dec 2015 16:09:04 +1100
Subject: [PATCH] Extract common base class for SSLAuthenticatorWithFallback

Two Tomcat version-specific implementations of
SSLAuthenticatorWithFallback exist, with much duplicate code.
Extract an abstract base class 'AbstractPKIAuthenticator' and
implement just the unique bits in the concrete classes.

Part of: https://fedorahosted.org/pki/ticket/1359
---
 .../cms/tomcat/AbstractPKIAuthenticator.java}      |  35 +++--
 base/server/tomcat7/src/CMakeLists.txt             |   3 +
 .../cms/tomcat/SSLAuthenticatorWithFallback.java   | 143 +++------------------
 base/server/tomcat8/src/CMakeLists.txt             |   3 +
 .../cms/tomcat/SSLAuthenticatorWithFallback.java   | 140 ++------------------
 5 files changed, 51 insertions(+), 273 deletions(-)
 copy base/server/{tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java => tomcat/src/com/netscape/cms/tomcat/AbstractPKIAuthenticator.java} (86%)

diff --git a/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java b/base/server/tomcat/src/com/netscape/cms/tomcat/AbstractPKIAuthenticator.java
similarity index 86%
copy from base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
copy to base/server/tomcat/src/com/netscape/cms/tomcat/AbstractPKIAuthenticator.java
index 3678791b927a9d6bca523d6a79a5fbfff1b675cf..f98377dc2322d7fa525d360a401348468f840f43 100644
--- a/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
+++ b/base/server/tomcat/src/com/netscape/cms/tomcat/AbstractPKIAuthenticator.java
@@ -28,6 +28,7 @@ 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;
 import org.apache.catalina.authenticator.AuthenticatorBase;
 import org.apache.catalina.authenticator.BasicAuthenticator;
 import org.apache.catalina.authenticator.FormAuthenticator;
@@ -37,7 +38,7 @@ import org.apache.catalina.connector.Request;
 /**
  * @author Endi S. Dewata
  */
-public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
+public abstract class AbstractPKIAuthenticator extends AuthenticatorBase {
 
     public final static String BASIC_AUTHENTICATOR = "BASIC";
     public final static String FORM_AUTHENTICATOR = "FORM";
@@ -47,7 +48,7 @@ public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
     AuthenticatorBase sslAuthenticator = new SSLAuthenticator();
     AuthenticatorBase fallbackAuthenticator = new BasicAuthenticator();
 
-    public SSLAuthenticatorWithFallback() {
+    public AbstractPKIAuthenticator() {
         log("Creating SSL authenticator with fallback");
     }
 
@@ -68,9 +69,7 @@ public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
 
     }
 
-    @Override
-    public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
-
+    public boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException {
         X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
         boolean result;
 
@@ -84,7 +83,7 @@ public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
                     log("SSL auth return code: "+code);
                 }
             };
-            result = sslAuthenticator.authenticate(request, wrapper);
+            result = doSubAuthenticate(sslAuthenticator, request, wrapper);
 
         } else {
             log("Authenticating with "+fallbackMethod+" authentication");
@@ -96,30 +95,28 @@ public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
                     log("Fallback auth return code: "+code);
                 }
             };
-            result = fallbackAuthenticator.authenticate(request, wrapper);
+            result = doSubAuthenticate(fallbackAuthenticator, request, wrapper);
         }
 
         if (result)
             return true;
 
         log("Result: "+result);
-        String realmName = AuthenticatorBase.getRealmName(request.getContext());
-
-
-        StringBuilder value = new StringBuilder(16);
-        value.append("Basic realm=\"");
-        if (realmName != null) {
-            value.append(REALM_NAME);
-        } else {
-            value.append(realmName);
-        }
-        value.append('\"');
-        response.setHeader(AUTH_HEADER_NAME, value.toString());
+        String realmName = doGetRealmName(request);
+        response.setHeader(AUTH_HEADER_NAME,
+            "Basic realm=\"" + (realmName == null ? REALM_NAME : realmName) + "\"");
         response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
 
         return false;
     }
 
+    public abstract boolean doSubAuthenticate(
+        Authenticator auth, Request req, HttpServletResponse resp)
+        throws IOException;
+
+    public abstract String doGetRealmName(Request req);
+
+
     @Override
     protected String getAuthMethod() {
         return HttpServletRequest.CLIENT_CERT_AUTH;
diff --git a/base/server/tomcat7/src/CMakeLists.txt b/base/server/tomcat7/src/CMakeLists.txt
index 77293a654d5335ad134afaac30e4678360e0cc05..bb42bfe0a4a840f0b271a83600f79686f76cc353 100644
--- a/base/server/tomcat7/src/CMakeLists.txt
+++ b/base/server/tomcat7/src/CMakeLists.txt
@@ -124,8 +124,11 @@ javac(pki-tomcat7-classes
         com/netscape/cms/tomcat/*.java
     CLASSPATH
         ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_SCAN_JAR}
+            ${CMAKE_BINARY_DIR}/../../tomcat
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/../../tomcat
+    DEPENDS
+        pki-tomcat-classes
 )
 
 configure_file(
diff --git a/base/server/tomcat7/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java b/base/server/tomcat7/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
index 20bf85d221bac3f5dbd1cac73aa9b8252a1cc6e8..38c2431d8ed0775bb2ca2b5c32ca87fc07b180c5 100644
--- a/base/server/tomcat7/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
+++ b/base/server/tomcat7/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
@@ -19,154 +19,47 @@
 package com.netscape.cms.tomcat;
 
 import java.io.IOException;
-import java.security.cert.X509Certificate;
+import java.lang.ThreadLocal;
 
-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.Authenticator;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.deploy.LoginConfig;
 
 /**
  * @author Endi S. Dewata
  */
-public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
+public class SSLAuthenticatorWithFallback extends AbstractPKIAuthenticator {
 
-    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");
-    }
+    protected static final ThreadLocal<LoginConfig> loginConfig =
+        new ThreadLocal<>();
 
     @Override
     public String getInfo() {
         return "SSL authenticator with "+fallbackMethod+" fallback.";
     }
 
-    public String getFallbackMethod() {
-        return fallbackMethod;
+    @Override
+    public boolean doSubAuthenticate(
+            Authenticator auth, Request req, HttpServletResponse resp)
+            throws IOException {
+        return auth.authenticate(req, resp, loginConfig.get());
     }
 
-    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 String doGetRealmName(Request request /* ignored */) {
+        return loginConfig.get().getRealmName();
     }
 
     @Override
     public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
-
-        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+" authentication");
-            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);
+        loginConfig.set(config);
+        try {
+            return doAuthenticate(request, response);
+        } finally {
+            loginConfig.remove();
         }
-
-        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.setAlwaysUseSession(alwaysUseSession);
-        sslAuthenticator.init();
-
-        fallbackAuthenticator.setAlwaysUseSession(alwaysUseSession);
-        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);
-    }
 }
diff --git a/base/server/tomcat8/src/CMakeLists.txt b/base/server/tomcat8/src/CMakeLists.txt
index a2badac69837023ca7078cfeaa80cdf98ba2a63b..df55916bc0bc0e2bb010239115c68b8bc5ad6c84 100644
--- a/base/server/tomcat8/src/CMakeLists.txt
+++ b/base/server/tomcat8/src/CMakeLists.txt
@@ -124,8 +124,11 @@ javac(pki-tomcat8-classes
         com/netscape/cms/tomcat/*.java
     CLASSPATH
         ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_SCAN_JAR}
+            ${CMAKE_BINARY_DIR}/../../tomcat
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/../../tomcat
+    DEPENDS
+        pki-tomcat-classes
 )
 
 configure_file(
diff --git a/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java b/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
index 3678791b927a9d6bca523d6a79a5fbfff1b675cf..12ca0bb7c5213f8e833f75bfb92826dbc8718d87 100644
--- a/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
+++ b/base/server/tomcat8/src/com/netscape/cms/tomcat/SSLAuthenticatorWithFallback.java
@@ -19,150 +19,32 @@
 package com.netscape.cms.tomcat;
 
 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.Authenticator;
 import org.apache.catalina.connector.Request;
 
 /**
  * @author Endi S. Dewata
  */
-public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
+public class SSLAuthenticatorWithFallback extends AbstractPKIAuthenticator {
 
-    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");
-    }
-
-    public String getFallbackMethod() {
-        return fallbackMethod;
+    @Override
+    public boolean doSubAuthenticate(
+            Authenticator auth, Request req, HttpServletResponse resp)
+            throws IOException {
+        return auth.authenticate(req, resp);
     }
 
-    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 String doGetRealmName(Request request) {
+        return getRealmName(request.getContext());
     }
 
     @Override
     public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
-
-        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);
-
-        } else {
-            log("Authenticating with "+fallbackMethod+" authentication");
-            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);
-        }
-
-        if (result)
-            return true;
-
-        log("Result: "+result);
-        String realmName = AuthenticatorBase.getRealmName(request.getContext());
-
-
-        StringBuilder value = new StringBuilder(16);
-        value.append("Basic realm=\"");
-        if (realmName != null) {
-            value.append(REALM_NAME);
-        } else {
-            value.append(realmName);
-        }
-        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);
+        return doAuthenticate(request, response);
     }
 
-    @Override
-    protected void initInternal() throws LifecycleException {
-        log("Initializing authenticators");
-
-        super.initInternal();
-
-        sslAuthenticator.setAlwaysUseSession(alwaysUseSession);
-        sslAuthenticator.init();
-
-        fallbackAuthenticator.setAlwaysUseSession(alwaysUseSession);
-        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);
-    }
 }
-- 
2.5.0



More information about the Pki-devel mailing list