You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2016/02/15 15:45:58 UTC

svn commit: r1730541 - in /tomcat/trunk: java/org/apache/catalina/authenticator/AuthenticatorBase.java webapps/docs/changelog.xml

Author: markt
Date: Mon Feb 15 14:45:57 2016
New Revision: 1730541

URL: http://svn.apache.org/viewvc?rev=1730541&view=rev
Log:
First pass at adding JASPIC support.
- docs still TODO
- need to handle parallel deployment

Modified:
    tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java?rev=1730541&r1=1730540&r2=1730541&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java Mon Feb 15 14:45:57 2016
@@ -22,7 +22,19 @@ import java.security.cert.X509Certificat
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
+import javax.security.auth.Subject;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.config.AuthConfigFactory;
+import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.RegistrationListener;
+import javax.security.auth.message.config.ServerAuthConfig;
+import javax.security.auth.message.config.ServerAuthContext;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -38,6 +50,8 @@ import org.apache.catalina.Realm;
 import org.apache.catalina.Session;
 import org.apache.catalina.Valve;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl;
+import org.apache.catalina.authenticator.jaspic.MessageInfoImpl;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.realm.GenericPrincipal;
@@ -70,7 +84,8 @@ import org.apache.tomcat.util.res.String
  *
  * @author Craig R. McClanahan
  */
-public abstract class AuthenticatorBase extends ValveBase implements Authenticator {
+public abstract class AuthenticatorBase extends ValveBase
+        implements Authenticator, RegistrationListener {
 
     private static final Log log = LogFactory.getLog(AuthenticatorBase.class);
 
@@ -201,6 +216,10 @@ public abstract class AuthenticatorBase
      */
     protected SingleSignOn sso = null;
 
+    private volatile String jaspicAppContextID = null;
+    private volatile AuthConfigProvider jaspicProvider = null;
+
+
     // ------------------------------------------------------------- Properties
 
     public boolean getAlwaysUseSession() {
@@ -319,10 +338,9 @@ public abstract class AuthenticatorBase
      * Set the value of the flag that states if we should change the session ID
      * of an existing session upon successful authentication.
      *
-     * @param changeSessionIdOnAuthentication
-     *            <code>true</code> to change session ID upon successful
-     *            authentication, <code>false</code> to do not perform the
-     *            change.
+     * @param changeSessionIdOnAuthentication <code>true</code> to change
+     *            session ID upon successful authentication, <code>false</code>
+     *            to do not perform the change.
      */
     public void setChangeSessionIdOnAuthentication(boolean changeSessionIdOnAuthentication) {
         this.changeSessionIdOnAuthentication = changeSessionIdOnAuthentication;
@@ -410,6 +428,10 @@ public abstract class AuthenticatorBase
                     request.getRequestURI());
         }
 
+        AuthConfigProvider jaspicProvider = getJaspicProvider();
+        MessageInfo messageInfo = null;
+        ServerAuthContext serverAuthContext = null;
+
         // Have we got a cached authenticated Principal to record?
         if (cache) {
             Principal principal = request.getUserPrincipal();
@@ -429,6 +451,20 @@ public abstract class AuthenticatorBase
             }
         }
 
+        if (jaspicProvider != null) {
+            messageInfo = new MessageInfoImpl(request.getRequest(), response.getResponse(), true);
+            try {
+                ServerAuthConfig serverAuthConfig = jaspicProvider.getServerAuthConfig(
+                        "HttpServlet", jaspicAppContextID, new CallbackHandlerImpl());
+                String authContextID = serverAuthConfig.getAuthContextID(messageInfo);
+                serverAuthContext = serverAuthConfig.getAuthContext(authContextID, null, null);
+            } catch (AuthException e) {
+                // TODO: i18n log this
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                return;
+            }
+        }
+
         // Special handling for form-based logins to deal with the case
         // where the login form (and therefore the "j_security_check" URI
         // to which it submits) might be outside the secured area
@@ -436,7 +472,7 @@ public abstract class AuthenticatorBase
         String decodedRequestURI = request.getDecodedRequestURI();
         if (decodedRequestURI.startsWith(contextPath) &&
                 decodedRequestURI.endsWith(Constants.FORM_ACTION)) {
-            if (!authenticate(request, response)) {
+            if (!authenticate(request, response, serverAuthContext, messageInfo)) {
                 if (log.isDebugEnabled()) {
                     log.debug(" Failed authenticate() test ??" + decodedRequestURI);
                 }
@@ -478,7 +514,8 @@ public abstract class AuthenticatorBase
         // Is this request URI subject to a security constraint?
         SecurityConstraint[] constraints = realm.findSecurityConstraints(request, this.context);
 
-        if (constraints == null && !context.getPreemptiveAuthentication()) {
+        if (constraints == null && !context.getPreemptiveAuthentication() &&
+                jaspicProvider == null) {
             if (log.isDebugEnabled()) {
                 log.debug(" Not subject to any constraint");
             }
@@ -551,11 +588,15 @@ public abstract class AuthenticatorBase
             authRequired = certs != null && certs.length > 0;
         }
 
+        if (!authRequired && jaspicProvider != null) {
+            authRequired = true;
+        }
+
         if (authRequired) {
             if (log.isDebugEnabled()) {
                 log.debug(" Calling authenticate()");
             }
-            if (!authenticate(request, response)) {
+            if (!authenticate(request, response, serverAuthContext, messageInfo)) {
                 if (log.isDebugEnabled()) {
                     log.debug(" Failed authenticate() test");
                 }
@@ -590,6 +631,15 @@ public abstract class AuthenticatorBase
         }
         getNext().invoke(request, response);
 
+        if (serverAuthContext != null && messageInfo != null) {
+            try {
+                serverAuthContext.secureResponse(messageInfo, null);
+                request.setRequest((HttpServletRequest) messageInfo.getRequestMessage());
+                response.setResponse((HttpServletResponse) messageInfo.getResponseMessage());
+            } catch (AuthException e) {
+                // TODO Log this. Change the status code?
+            }
+        }
     }
 
     // ------------------------------------------------------ Protected Methods
@@ -641,6 +691,66 @@ public abstract class AuthenticatorBase
 
     }
 
+
+    private boolean authenticate(Request request, Response response,
+            ServerAuthContext serverAuthContext, MessageInfo messageInfo) throws IOException {
+
+        if (serverAuthContext == null) {
+            // No JASPIC configuration. Use the standard authenticator.
+            return authenticate(request, response);
+        } else {
+            checkForCachedAuthentication(request, response, false);
+            Subject client = new Subject();
+            AuthStatus authStatus;
+            try {
+                authStatus = serverAuthContext.validateRequest(messageInfo, client, null);
+            } catch (AuthException e) {
+                log.debug(sm.getString("authenticator.loginFail"), e);
+                return false;
+            }
+
+            request.setRequest((HttpServletRequest) messageInfo.getRequestMessage());
+            response.setResponse((HttpServletResponse) messageInfo.getResponseMessage());
+
+            if (authStatus == AuthStatus.SUCCESS) {
+                GenericPrincipal principal = getPrincipal(client);
+                if (log.isDebugEnabled()) {
+                    log.debug("Authenticated user: " + principal);
+                }
+                if (principal == null) {
+                    request.setUserPrincipal(null);
+                    request.setAuthType(null);
+                } else {
+                    request.setNote(Constants.REQ_JASPIC_SUBJECT_NOTE, client);
+                    @SuppressWarnings("rawtypes")// JASPIC API uses raw types
+                    Map map = messageInfo.getMap();
+                    if (map != null && map.containsKey("javax.servlet.http.registerSession")) {
+                        register(request, response, principal, "JASPIC", null, null, true, true);
+                    } else {
+                        register(request, response, principal, "JASPIC", null, null);
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+
+
+    private GenericPrincipal getPrincipal(Subject subject) {
+        if (subject == null) {
+            return null;
+        }
+
+        Set<GenericPrincipal> principals = subject.getPrivateCredentials(GenericPrincipal.class);
+        if (principals.isEmpty()) {
+            return null;
+        }
+
+        return principals.iterator().next();
+    }
+
+
     /**
      * Check to see if the user has already been authenticated earlier in the
      * processing chain or if there is enough information available to
@@ -782,6 +892,13 @@ public abstract class AuthenticatorBase
      */
     public void register(Request request, HttpServletResponse response, Principal principal,
             String authType, String username, String password) {
+        register(request, response, principal, authType, username, password, alwaysUseSession, cache);
+    }
+
+
+    private void register(Request request, HttpServletResponse response, Principal principal,
+            String authType, String username, String password, boolean alwaysUseSession,
+            boolean cache) {
 
         if (log.isDebugEnabled()) {
             String name = (principal == null) ? "none" : principal.getName();
@@ -925,10 +1042,30 @@ public abstract class AuthenticatorBase
 
     @Override
     public void logout(Request request) {
-        register(request, request.getResponse(), null, null, null, null);
+        AuthConfigProvider provider = getJaspicProvider();
+        if (provider != null) {
+            MessageInfo messageInfo = new MessageInfoImpl(request, request.getResponse(), true);
+            Subject client = (Subject) request.getNote(Constants.REQ_JASPIC_SUBJECT_NOTE);
+            if (client == null) {
+                return;
+            }
 
+            ServerAuthContext serverAuthContext;
+            try {
+                ServerAuthConfig serverAuthConfig = provider.getServerAuthConfig("HttpServlet",
+                        jaspicAppContextID, new CallbackHandlerImpl());
+                String authContextID = serverAuthConfig.getAuthContextID(messageInfo);
+                serverAuthContext = serverAuthConfig.getAuthContext(authContextID, null, null);
+                serverAuthContext.cleanSubject(messageInfo, client);
+            } catch (AuthException e) {
+                log.debug(sm.getString("authenticator.jaspicCleanSubjectFail"), e);
+            }
+        }
+
+        register(request, request.getResponse(), null, null, null, null);
     }
 
+
     /**
      * Start this component and implement the requirements of
      * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
@@ -940,6 +1077,11 @@ public abstract class AuthenticatorBase
     @Override
     protected synchronized void startInternal() throws LifecycleException {
 
+        // TODO: Handle JASPIC and parallel deployment
+        ServletContext servletContext = context.getServletContext();
+        jaspicAppContextID = servletContext.getVirtualServerName() + " " +
+                servletContext.getContextPath();
+
         // Look up the SingleSignOn implementation in our request processing
         // path, if there is one
         Container parent = context.getParent();
@@ -986,4 +1128,26 @@ public abstract class AuthenticatorBase
 
         sso = null;
     }
+
+
+    private AuthConfigProvider getJaspicProvider() {
+        AuthConfigProvider provider = jaspicProvider;
+        if (provider == null) {
+            AuthConfigFactory factory = AuthConfigFactory.getFactory();
+            provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID, this);
+            if (provider != null) {
+                jaspicProvider = provider;
+            }
+        }
+        return provider;
+    }
+
+
+    @Override
+    public void notify(String layer, String appContext) {
+        AuthConfigFactory factory = AuthConfigFactory.getFactory();
+        AuthConfigProvider provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID,
+                this);
+        jaspicProvider = provider;
+    }
 }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1730541&r1=1730540&r2=1730541&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Mon Feb 15 14:45:57 2016
@@ -73,6 +73,9 @@
         It throws a StringIndexOutOfBoundsException if the name is exactly
         "org" or "javax". (rjung)
       </fix>
+      <add>
+        Add JASPIC (JSR-196) support. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org