You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2016/12/21 15:06:56 UTC

[1/2] cxf-fediz git commit: Refactored Jetty plugins

Repository: cxf-fediz
Updated Branches:
  refs/heads/master 25dcd2754 -> e8889b438


Refactored Jetty plugins


Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/db80f269
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/db80f269
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/db80f269

Branch: refs/heads/master
Commit: db80f26987cace847305f31203237a6b7c4fbfe9
Parents: 25dcd27
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Dec 21 11:27:13 2016 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Dec 21 11:27:13 2016 +0000

----------------------------------------------------------------------
 .../fediz/jetty8/FederationAuthenticator.java   | 367 ++++++++++---------
 .../fediz/jetty9/FederationAuthenticator.java   | 367 ++++++++++---------
 2 files changed, 386 insertions(+), 348 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/db80f269/plugins/jetty8/src/main/java/org/apache/cxf/fediz/jetty8/FederationAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/jetty8/src/main/java/org/apache/cxf/fediz/jetty8/FederationAuthenticator.java b/plugins/jetty8/src/main/java/org/apache/cxf/fediz/jetty8/FederationAuthenticator.java
index 56656a0..803c26a 100644
--- a/plugins/jetty8/src/main/java/org/apache/cxf/fediz/jetty8/FederationAuthenticator.java
+++ b/plugins/jetty8/src/main/java/org/apache/cxf/fediz/jetty8/FederationAuthenticator.java
@@ -79,7 +79,6 @@ import org.eclipse.jetty.util.log.Logger;
  * associated with the session.
  * </p>
  */
-// CHECKSTYLE:OFF
 public class FederationAuthenticator extends LoginAuthenticator {
     
     public static final String J_URI = "org.eclipse.jetty.security.form_URI";
@@ -166,10 +165,11 @@ public class FederationAuthenticator extends LoginAuthenticator {
         // Check to see if it is a metadata request
         MetadataDocumentHandler mdHandler = new MetadataDocumentHandler(fedConfig);
         if (mdHandler.canHandleRequest(request)) {
+            Authentication authentication = Authentication.SEND_FAILURE;
             if (mdHandler.handleRequest(request, response)) {
-                return Authentication.SEND_CONTINUE;
+                authentication = Authentication.SEND_CONTINUE;
             }
-            return Authentication.SEND_FAILURE;
+            return authentication;
         }
 
         if (!mandatory) {
@@ -182,199 +182,54 @@ public class FederationAuthenticator extends LoginAuthenticator {
             LOG.warn("Unsupported encoding '" + this.encoding + "'", ex);
         }
         
-        String uri = request.getRequestURI();
-        if (uri == null) {
-            uri = URIUtil.SLASH;
-        }
-
         try {
             String action = request.getParameter(FederationConstants.PARAM_ACTION);
-            String responseToken = getResponseToken(request, fedConfig);
+            Authentication authentication = null;
             
             // Handle a request for authentication.
             if (isSignInRequest(request, fedConfig)) {
-
-                FedizResponse wfRes = null;
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("SignIn request found");
-                }
-
-                if (responseToken == null) {
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("SignIn request must contain a response token from the IdP");
-                    }
-                    response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                    return Authentication.SEND_FAILURE;
-                } else {
-
-                    FedizRequest wfReq = new FedizRequest();
-                    wfReq.setAction(action);
-                    wfReq.setResponseToken(responseToken);
-                    wfReq.setState(request.getParameter("RelayState"));
-                    wfReq.setRequest(request);
-
-                    X509Certificate certs[] = 
-                        (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
-                    wfReq.setCerts(certs);
-
-                    FederationLoginService fedLoginService = (FederationLoginService)this._loginService;
-                    UserIdentity user = fedLoginService.login(null, wfReq, fedConfig);
-                    if (user != null)
-                    {
-                        session=renewSession(request,response);
-
-                        // Redirect to original request
-                        String nuri;
-                        synchronized(session)
-                        {
-                            // Check the context
-                            String savedContext = (String) session.getAttribute(J_CONTEXT);
-                            String receivedContext = request.getParameter(FederationConstants.PARAM_CONTEXT);
-                            if (savedContext == null || !savedContext.equals(receivedContext)) {
-                                LOG.warn("The received wctx parameter does not match the saved value");
-                                response.sendError(HttpServletResponse.SC_FORBIDDEN);
-                                return Authentication.UNAUTHENTICATED;
-                            }
-                            
-                            nuri = (String) session.getAttribute(J_URI);
-
-                            if (nuri == null || nuri.length() == 0)
-                            {
-                                nuri = request.getContextPath();
-                                if (nuri.length() == 0) { 
-                                    nuri = URIUtil.SLASH;
-                                }
-                            }
-                            Authentication cached=new SessionAuthentication(getAuthMethod(), user, wfRes);
-                            session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
-                        }
-                        
-                        FederationUserIdentity fui = (FederationUserIdentity)user;
-                        session.setAttribute(SECURITY_TOKEN_ATTR, fui.getToken());
-                        
-                        response.setContentLength(0);   
-                        response.sendRedirect(response.encodeRedirectURL(nuri));
-
-                        return new FederationAuthentication(getAuthMethod(), user);
-                    }
-
-                    // not authenticated
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("WSFED authentication FAILED");
-                    }
-                    if (response != null) {
-                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
-                        return Authentication.UNAUTHENTICATED;
-                    }
-
-                }
+                authentication = handleSignInRequest(request, response, session, fedConfig);
             } else if (FederationConstants.ACTION_SIGNOUT_CLEANUP.equals(action)) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("SignOutCleanup request found");
-                    LOG.debug("SignOutCleanup action...");
-                }
-                session.invalidate();
-
-                final ServletOutputStream responseOutputStream = response.getOutputStream();
-                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("logout.jpg");
-                if (inputStream == null) {
-                    LOG.warn("Could not write logout.jpg");
-                    return Authentication.SEND_FAILURE;
-                }
-                int read = 0;
-                byte[] buf = new byte[1024];
-                while ((read = inputStream.read(buf)) != -1) {
-                    responseOutputStream.write(buf, 0, read);
-                }
-                inputStream.close();
-                responseOutputStream.flush();
-                return Authentication.SEND_SUCCESS;
+                authentication = handleSignOutCleanup(response, session);
             } else if (!FederationConstants.ACTION_SIGNOUT.equals(action) && action != null) {
                 LOG.warn("Not supported action found in parameter wa: " + action);
                 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                return Authentication.UNAUTHENTICATED;
+                authentication = Authentication.UNAUTHENTICATED;
+            }
+            
+            if (authentication != null) {
+                return authentication;
             }
 
             // Look for cached authentication
-            Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
-            if (authentication != null) 
-            {
-                // Has authentication been revoked?
-                if (authentication instanceof Authentication.User
-                    && isTokenExpired(fedConfig, ((Authentication.User)authentication).getUserIdentity())) {
-                    session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
-                }
-                else
-                {
-                    //logout
-                    boolean logout = FederationConstants.ACTION_SIGNOUT.equals(action);
-                    String logoutUrl = fedConfig.getLogoutURL();
-                    if (logout || logoutUrl != null && !logoutUrl.isEmpty() && uri.equals(contextName + logoutUrl)) {
-                        session.invalidate();
-
-                        FedizProcessor wfProc = 
-                            FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol());
-                        signOutRedirectToIssuer(request, response, wfProc);
-
-                        return Authentication.SEND_CONTINUE;
-                    }
-
-                    String j_uri = (String)session.getAttribute(J_URI);
-                    if (j_uri != null)
-                    {
-                        @SuppressWarnings("unchecked")
-                        MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(J_POST);
-                        if (j_post != null)
-                        {
-                            StringBuffer buf = request.getRequestURL();
-                            if (request.getQueryString() != null) {
-                                buf.append("?").append(request.getQueryString());
-                            }
-
-                            if (j_uri.equals(buf.toString()))
-                            {
-                                // This is a retry of an original POST request
-                                // so restore method and parameters
-
-                                session.removeAttribute(J_POST);                        
-                                Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
-                                base_request.setMethod(HttpMethods.POST);
-                                base_request.setParameters(j_post);
-                            }
-                        }
-                        else
-                            session.removeAttribute(J_URI);
-                            
-                    }
-                    return authentication;
-                }
-            }          
+            authentication = handleCachedAuthentication(request, response, session, fedConfig);
+            if (authentication != null) {
+                return authentication;
+            }
             
 
             // if we can't send challenge
-            if (DeferredAuthentication.isDeferred(response))
-            {
-                LOG.debug("auth deferred {}",session.getId());
+            if (DeferredAuthentication.isDeferred(response)) {
+                LOG.debug("auth deferred {}", session.getId());
                 return Authentication.UNAUTHENTICATED;
             }
             
             // remember the current URI
-            synchronized (session)
-            {
+            synchronized (session) {
                 // But only if it is not set already, or we save every uri that leads to a login form redirect
-                if (session.getAttribute(J_URI)==null) // || alwaysSaveUri)
-                {  
+                if (session.getAttribute(J_URI) == null) { // || alwaysSaveUri)  
                     StringBuffer buf = request.getRequestURL();
                     if (request.getQueryString() != null) {
                         buf.append("?").append(request.getQueryString());
                     }
                     session.setAttribute(J_URI, buf.toString());
                     
-                    if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(req.getContentType()) && HttpMethods.POST.equals(request.getMethod()))
-                    {
-                        Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
-                        base_request.extractParameters();                        
-                        session.setAttribute(J_POST, new MultiMap<String>(base_request.getParameters()));
+                    if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(req.getContentType()) 
+                        && HttpMethods.POST.equals(request.getMethod())) {
+                        Request baseRequest = (req instanceof Request) ? (Request)req 
+                            : AbstractHttpConnection.getCurrentConnection().getRequest();
+                        baseRequest.extractParameters();                        
+                        session.setAttribute(J_POST, new MultiMap<String>(baseRequest.getParameters()));
                     }
                 }
             }
@@ -393,6 +248,170 @@ public class FederationAuthenticator extends LoginAuthenticator {
          */
     }
     
+    private Authentication handleSignInRequest(HttpServletRequest request, HttpServletResponse response, 
+                                               HttpSession session, FedizContext fedConfig) throws IOException {
+        FedizResponse wfRes = null;
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("SignIn request found");
+        }
+
+        String action = request.getParameter(FederationConstants.PARAM_ACTION);
+        String responseToken = getResponseToken(request, fedConfig);
+        if (responseToken == null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("SignIn request must contain a response token from the IdP");
+            }
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return Authentication.SEND_FAILURE;
+        } else {
+
+            FedizRequest wfReq = new FedizRequest();
+            wfReq.setAction(action);
+            wfReq.setResponseToken(responseToken);
+            wfReq.setState(request.getParameter("RelayState"));
+            wfReq.setRequest(request);
+
+            X509Certificate[] certs = 
+                (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
+            wfReq.setCerts(certs);
+
+            FederationLoginService fedLoginService = (FederationLoginService)this._loginService;
+            UserIdentity user = fedLoginService.login(null, wfReq, fedConfig);
+            if (user != null) {
+                session = renewSession(request, response);
+
+                // Redirect to original request
+                String nuri;
+                synchronized (session) {
+                    // Check the context
+                    String savedContext = (String) session.getAttribute(J_CONTEXT);
+                    String receivedContext = request.getParameter(FederationConstants.PARAM_CONTEXT);
+                    if (savedContext == null || !savedContext.equals(receivedContext)) {
+                        LOG.warn("The received wctx parameter does not match the saved value");
+                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
+                        return Authentication.UNAUTHENTICATED;
+                    }
+                    
+                    nuri = (String) session.getAttribute(J_URI);
+
+                    if (nuri == null || nuri.length() == 0) {
+                        nuri = request.getContextPath();
+                        if (nuri.length() == 0) { 
+                            nuri = URIUtil.SLASH;
+                        }
+                    }
+                    Authentication cached = new SessionAuthentication(getAuthMethod(), user, wfRes);
+                    session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
+                }
+                
+                FederationUserIdentity fui = (FederationUserIdentity)user;
+                session.setAttribute(SECURITY_TOKEN_ATTR, fui.getToken());
+                
+                response.setContentLength(0);   
+                response.sendRedirect(response.encodeRedirectURL(nuri));
+
+                return new FederationAuthentication(getAuthMethod(), user);
+            }
+
+            // not authenticated
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("WSFED authentication FAILED");
+            }
+            if (response != null) {
+                response.sendError(HttpServletResponse.SC_FORBIDDEN);
+            }
+            return Authentication.UNAUTHENTICATED;
+        }
+    }
+    
+    private Authentication handleSignOutCleanup(HttpServletResponse response, HttpSession session) throws IOException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("SignOutCleanup request found");
+            LOG.debug("SignOutCleanup action...");
+        }
+        session.invalidate();
+
+        final ServletOutputStream responseOutputStream = response.getOutputStream();
+        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("logout.jpg");
+        if (inputStream == null) {
+            LOG.warn("Could not write logout.jpg");
+            return Authentication.SEND_FAILURE;
+        }
+        int read = 0;
+        byte[] buf = new byte[1024];
+        while ((read = inputStream.read(buf)) != -1) {
+            responseOutputStream.write(buf, 0, read);
+        }
+        inputStream.close();
+        responseOutputStream.flush();
+        return Authentication.SEND_SUCCESS;
+    }
+    
+    private Authentication handleCachedAuthentication(HttpServletRequest request, HttpServletResponse response, 
+                                                      HttpSession session, FedizContext fedConfig) throws IOException {
+        Authentication authentication = 
+            (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
+        if (authentication != null) {
+            // Has authentication been revoked?
+            if (authentication instanceof Authentication.User
+                && isTokenExpired(fedConfig, ((Authentication.User)authentication).getUserIdentity())) {
+                session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
+            } else {
+                //logout
+                String action = request.getParameter(FederationConstants.PARAM_ACTION);
+                boolean logout = FederationConstants.ACTION_SIGNOUT.equals(action);
+                String logoutUrl = fedConfig.getLogoutURL();
+                
+                String uri = request.getRequestURI();
+                if (uri == null) {
+                    uri = URIUtil.SLASH;
+                }
+                
+                String contextName = request.getSession().getServletContext().getContextPath();
+                if (contextName == null || contextName.isEmpty()) {
+                    contextName = "/";
+                }
+                
+                if (logout || logoutUrl != null && !logoutUrl.isEmpty() && uri.equals(contextName + logoutUrl)) {
+                    session.invalidate();
+
+                    FedizProcessor wfProc = 
+                        FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol());
+                    signOutRedirectToIssuer(request, response, wfProc);
+
+                    return Authentication.SEND_CONTINUE;
+                }
+
+                String jUri = (String)session.getAttribute(J_URI);
+                @SuppressWarnings("unchecked")
+                MultiMap<String> jPost = (MultiMap<String>)session.getAttribute(J_POST);
+                if (jUri != null && jPost != null) {
+                    StringBuffer buf = request.getRequestURL();
+                    if (request.getQueryString() != null) {
+                        buf.append("?").append(request.getQueryString());
+                    }
+
+                    if (jUri.equals(buf.toString())) {
+                        // This is a retry of an original POST request
+                        // so restore method and parameters
+
+                        session.removeAttribute(J_POST);            
+                        Request baseRequest = (Request)request; 
+                        // (req instanceof Request)?(Request)
+                        // req:HttpConnection.getCurrentConnection().getRequest();
+                        baseRequest.setMethod(HttpMethods.POST);
+                        baseRequest.setParameters(jPost);
+                    }
+                } else if (jUri != null) {
+                    session.removeAttribute(J_URI);
+                }
+                        
+                return authentication;
+            }
+        }
+        return null;
+    }
+    
     private boolean isTokenExpired(FedizContext fedConfig, UserIdentity userIdentity) {
         if (fedConfig.isDetectExpiredTokens()) {
             try {
@@ -463,7 +482,8 @@ public class FederationAuthenticator extends LoginAuthenticator {
      *             {@link HttpServletResponse#sendError(int, String)} throws an
      *             {@link IOException}
      */
-    protected void signInRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor, HttpSession session)
+    protected void signInRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, 
+                                          FedizProcessor processor, HttpSession session)
         throws IOException {
 
         //Not supported in jetty 7.6
@@ -484,7 +504,7 @@ public class FederationAuthenticator extends LoginAuthenticator {
                     }
                 }
                 
-                synchronized(session) {
+                synchronized (session) {
                     session.setAttribute(J_CONTEXT, redirectionResponse.getRequestState().getState());
                 }
                 
@@ -502,7 +522,8 @@ public class FederationAuthenticator extends LoginAuthenticator {
         
     }
 
-    protected void signOutRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor)
+    protected void signOutRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, 
+                                           FedizProcessor processor)
             throws IOException {
 
         //Not supported in jetty 7.6

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/db80f269/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java
index e845b08..d5ce68c 100644
--- a/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java
+++ b/plugins/jetty9/src/main/java/org/apache/cxf/fediz/jetty9/FederationAuthenticator.java
@@ -78,7 +78,6 @@ import org.eclipse.jetty.util.log.Logger;
  * associated with the session.
  * </p>
  */
-// CHECKSTYLE:OFF
 public class FederationAuthenticator extends LoginAuthenticator {
     
     public static final String J_URI = "org.eclipse.jetty.security.form_URI";
@@ -165,10 +164,11 @@ public class FederationAuthenticator extends LoginAuthenticator {
         // Check to see if it is a metadata request
         MetadataDocumentHandler mdHandler = new MetadataDocumentHandler(fedConfig);
         if (mdHandler.canHandleRequest(request)) {
+            Authentication authentication = Authentication.SEND_FAILURE;
             if (mdHandler.handleRequest(request, response)) {
-                return Authentication.SEND_CONTINUE;
+                authentication = Authentication.SEND_CONTINUE;
             }
-            return Authentication.SEND_FAILURE;
+            return authentication;
         }
 
         if (!mandatory) {
@@ -181,189 +181,41 @@ public class FederationAuthenticator extends LoginAuthenticator {
             LOG.warn("Unsupported encoding '" + this.encoding + "'", ex);
         }
         
-        String uri = request.getRequestURI();
-        if (uri == null) {
-            uri = URIUtil.SLASH;
-        }
-
         try {
             String action = request.getParameter(FederationConstants.PARAM_ACTION);
-            String responseToken = getResponseToken(request, fedConfig);
+            Authentication authentication = null;
             
             // Handle a request for authentication.
             if (isSignInRequest(request, fedConfig)) {
-
-                FedizResponse wfRes = null;
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("SignIn request found");
-                }
-
-                if (responseToken == null) {
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("SignIn request must contain a response token from the IdP");
-                    }
-                    response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                    return Authentication.SEND_FAILURE;
-                } else {
-
-                    FedizRequest wfReq = new FedizRequest();
-                    wfReq.setAction(action);
-                    wfReq.setResponseToken(responseToken);
-                    wfReq.setState(request.getParameter("RelayState"));
-                    wfReq.setRequest(request);
-
-                    X509Certificate certs[] = 
-                        (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
-                    wfReq.setCerts(certs);
-
-                    FederationLoginService fedLoginService = (FederationLoginService)this._loginService;
-                    UserIdentity user = fedLoginService.login(null, wfReq, fedConfig);
-                    if (user != null)
-                    {
-                        session=renewSession(request,response);
-
-                        // Redirect to original request
-                        String nuri;
-                        synchronized(session)
-                        {
-                            // Check the context
-                            String savedContext = (String) session.getAttribute(J_CONTEXT);
-                            String receivedContext = request.getParameter(FederationConstants.PARAM_CONTEXT);
-                            if (savedContext == null || !savedContext.equals(receivedContext)) {
-                                LOG.warn("The received wctx parameter does not match the saved value");
-                                response.sendError(HttpServletResponse.SC_FORBIDDEN);
-                                return Authentication.UNAUTHENTICATED;
-                            }
-                            
-                            nuri = (String) session.getAttribute(J_URI);
-
-                            if (nuri == null || nuri.length() == 0)
-                            {
-                                nuri = request.getContextPath();
-                                if (nuri.length() == 0) { 
-                                    nuri = URIUtil.SLASH;
-                                }
-                            }
-                            Authentication cached=new SessionAuthentication(getAuthMethod(), user, wfRes);
-                            session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
-                        }
-                        
-                        FederationUserIdentity fui = (FederationUserIdentity)user;
-                        session.setAttribute(SECURITY_TOKEN_ATTR, fui.getToken());
-                        
-                        response.setContentLength(0);   
-                        response.sendRedirect(response.encodeRedirectURL(nuri));
-
-                        return new FederationAuthentication(getAuthMethod(), user);
-                    }
-
-                    // not authenticated
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("WSFED authentication FAILED");
-                    }
-                    if (response != null) {
-                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
-                        return Authentication.UNAUTHENTICATED;
-                    }
-
-                }
+                authentication = handleSignInRequest(request, response, session, fedConfig);
             } else if (FederationConstants.ACTION_SIGNOUT_CLEANUP.equals(action)) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("SignOutCleanup request found");
-                    LOG.debug("SignOutCleanup action...");
-                }
-                session.invalidate();
-
-                final ServletOutputStream responseOutputStream = response.getOutputStream();
-                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("logout.jpg");
-                if (inputStream == null) {
-                    LOG.warn("Could not write logout.jpg");
-                    return Authentication.SEND_FAILURE;
-                }
-                int read = 0;
-                byte[] buf = new byte[1024];
-                while ((read = inputStream.read(buf)) != -1) {
-                    responseOutputStream.write(buf, 0, read);
-                }
-                inputStream.close();
-                responseOutputStream.flush();
-                return Authentication.SEND_SUCCESS;
+                authentication = handleSignOutCleanup(response, session);
             } else if (!FederationConstants.ACTION_SIGNOUT.equals(action) && action != null) {
                 LOG.warn("Not supported action found in parameter wa: " + action);
                 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                return Authentication.UNAUTHENTICATED;
+                authentication = Authentication.UNAUTHENTICATED;
+            }
+            
+            if (authentication != null) {
+                return authentication;
             }
 
             // Look for cached authentication
-            Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
-            if (authentication != null) 
-            {
-                // Has authentication been revoked?
-                if (authentication instanceof Authentication.User
-                    && isTokenExpired(fedConfig, ((Authentication.User)authentication).getUserIdentity())) {
-                    session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
-                }
-                else
-                {
-                    //logout
-                    boolean logout = FederationConstants.ACTION_SIGNOUT.equals(action);
-                    String logoutUrl = fedConfig.getLogoutURL();
-                    if (logout || logoutUrl != null && !logoutUrl.isEmpty() && uri.equals(contextName + logoutUrl)) {
-                        session.invalidate();
-
-                        FedizProcessor wfProc = 
-                            FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol());
-                        signOutRedirectToIssuer(request, response, wfProc);
-
-                        return Authentication.SEND_CONTINUE;
-                    }
-
-                    String j_uri = (String)session.getAttribute(J_URI);
-                    if (j_uri != null)
-                    {
-                        @SuppressWarnings("unchecked")
-                        MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(J_POST);
-                        if (j_post != null)
-                        {
-                            StringBuffer buf = request.getRequestURL();
-                            if (request.getQueryString() != null) {
-                                buf.append("?").append(request.getQueryString());
-                            }
-
-                            if (j_uri.equals(buf.toString()))
-                            {
-                                // This is a retry of an original POST request
-                                // so restore method and parameters
-
-                                session.removeAttribute(J_POST);            
-                                Request base_request = (Request)req; 
-                                    // (req instanceof Request)?(Request)req:HttpConnection.getCurrentConnection().getRequest();
-                                base_request.setMethod(HttpMethod.POST.asString());
-                                base_request.setQueryParameters(j_post);
-                            }
-                        }
-                        else
-                            session.removeAttribute(J_URI);
-                            
-                    }
-                    return authentication;
-                }
-            }          
-            
+            authentication = handleCachedAuthentication(request, response, session, fedConfig);
+            if (authentication != null) {
+                return authentication;
+            }
 
             // if we can't send challenge
-            if (DeferredAuthentication.isDeferred(response))
-            {
-                LOG.debug("auth deferred {}",session.getId());
+            if (DeferredAuthentication.isDeferred(response)) {
+                LOG.debug("auth deferred {}", session.getId());
                 return Authentication.UNAUTHENTICATED;
             }
             
             // remember the current URI
-            synchronized (session)
-            {
+            synchronized (session) {
                 // But only if it is not set already, or we save every uri that leads to a login form redirect
-                if (session.getAttribute(J_URI)==null) // || alwaysSaveUri)
-                {  
+                if (session.getAttribute(J_URI) == null) { // || alwaysSaveUri) 
                     StringBuffer buf = request.getRequestURL();
                     if (request.getQueryString() != null) {
                         buf.append("?").append(request.getQueryString());
@@ -371,12 +223,11 @@ public class FederationAuthenticator extends LoginAuthenticator {
                     session.setAttribute(J_URI, buf.toString());
                     
                     if (MimeTypes.Type.FORM_ENCODED.asString().equals(req.getContentType()) 
-                        && HttpMethod.POST.asString().equals(request.getMethod()))
-                    {
-                        Request base_request = (Request)req; 
+                        && HttpMethod.POST.asString().equals(request.getMethod())) {
+                        Request baseRequest = (Request)req; 
                             //(req instanceof Request)?(Request)req:HttpConnection.getCurrentConnection().getRequest();
-                        base_request.extractParameters();   
-                        session.setAttribute(J_POST, new MultiMap<String>(base_request.getQueryParameters()));
+                        baseRequest.extractParameters();   
+                        session.setAttribute(J_POST, new MultiMap<String>(baseRequest.getQueryParameters()));
                     }
                 }
             }
@@ -395,6 +246,170 @@ public class FederationAuthenticator extends LoginAuthenticator {
          */
     }
     
+    private Authentication handleSignInRequest(HttpServletRequest request, HttpServletResponse response, 
+                                               HttpSession session, FedizContext fedConfig) throws IOException {
+        FedizResponse wfRes = null;
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("SignIn request found");
+        }
+
+        String action = request.getParameter(FederationConstants.PARAM_ACTION);
+        String responseToken = getResponseToken(request, fedConfig);
+        if (responseToken == null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("SignIn request must contain a response token from the IdP");
+            }
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return Authentication.SEND_FAILURE;
+        } else {
+
+            FedizRequest wfReq = new FedizRequest();
+            wfReq.setAction(action);
+            wfReq.setResponseToken(responseToken);
+            wfReq.setState(request.getParameter("RelayState"));
+            wfReq.setRequest(request);
+
+            X509Certificate[] certs = 
+                (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
+            wfReq.setCerts(certs);
+
+            FederationLoginService fedLoginService = (FederationLoginService)this._loginService;
+            UserIdentity user = fedLoginService.login(null, wfReq, fedConfig);
+            if (user != null) {
+                session = renewSession(request, response);
+
+                // Redirect to original request
+                String nuri;
+                synchronized (session) {
+                    // Check the context
+                    String savedContext = (String) session.getAttribute(J_CONTEXT);
+                    String receivedContext = request.getParameter(FederationConstants.PARAM_CONTEXT);
+                    if (savedContext == null || !savedContext.equals(receivedContext)) {
+                        LOG.warn("The received wctx parameter does not match the saved value");
+                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
+                        return Authentication.UNAUTHENTICATED;
+                    }
+                    
+                    nuri = (String) session.getAttribute(J_URI);
+
+                    if (nuri == null || nuri.length() == 0) {
+                        nuri = request.getContextPath();
+                        if (nuri.length() == 0) { 
+                            nuri = URIUtil.SLASH;
+                        }
+                    }
+                    Authentication cached = new SessionAuthentication(getAuthMethod(), user, wfRes);
+                    session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
+                }
+                
+                FederationUserIdentity fui = (FederationUserIdentity)user;
+                session.setAttribute(SECURITY_TOKEN_ATTR, fui.getToken());
+                
+                response.setContentLength(0);   
+                response.sendRedirect(response.encodeRedirectURL(nuri));
+
+                return new FederationAuthentication(getAuthMethod(), user);
+            }
+
+            // not authenticated
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("WSFED authentication FAILED");
+            }
+            if (response != null) {
+                response.sendError(HttpServletResponse.SC_FORBIDDEN);
+            }
+            return Authentication.UNAUTHENTICATED;
+        }
+    }
+    
+    private Authentication handleSignOutCleanup(HttpServletResponse response, HttpSession session) throws IOException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("SignOutCleanup request found");
+            LOG.debug("SignOutCleanup action...");
+        }
+        session.invalidate();
+
+        final ServletOutputStream responseOutputStream = response.getOutputStream();
+        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("logout.jpg");
+        if (inputStream == null) {
+            LOG.warn("Could not write logout.jpg");
+            return Authentication.SEND_FAILURE;
+        }
+        int read = 0;
+        byte[] buf = new byte[1024];
+        while ((read = inputStream.read(buf)) != -1) {
+            responseOutputStream.write(buf, 0, read);
+        }
+        inputStream.close();
+        responseOutputStream.flush();
+        return Authentication.SEND_SUCCESS;
+    }
+    
+    private Authentication handleCachedAuthentication(HttpServletRequest request, HttpServletResponse response, 
+                                                      HttpSession session, FedizContext fedConfig) throws IOException {
+        Authentication authentication = 
+            (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
+        if (authentication != null) {
+            // Has authentication been revoked?
+            if (authentication instanceof Authentication.User
+                && isTokenExpired(fedConfig, ((Authentication.User)authentication).getUserIdentity())) {
+                session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
+            } else {
+                //logout
+                String action = request.getParameter(FederationConstants.PARAM_ACTION);
+                boolean logout = FederationConstants.ACTION_SIGNOUT.equals(action);
+                String logoutUrl = fedConfig.getLogoutURL();
+                
+                String uri = request.getRequestURI();
+                if (uri == null) {
+                    uri = URIUtil.SLASH;
+                }
+                
+                String contextName = request.getSession().getServletContext().getContextPath();
+                if (contextName == null || contextName.isEmpty()) {
+                    contextName = "/";
+                }
+                
+                if (logout || logoutUrl != null && !logoutUrl.isEmpty() && uri.equals(contextName + logoutUrl)) {
+                    session.invalidate();
+
+                    FedizProcessor wfProc = 
+                        FedizProcessorFactory.newFedizProcessor(fedConfig.getProtocol());
+                    signOutRedirectToIssuer(request, response, wfProc);
+
+                    return Authentication.SEND_CONTINUE;
+                }
+
+                String jUri = (String)session.getAttribute(J_URI);
+                @SuppressWarnings("unchecked")
+                MultiMap<String> jPost = (MultiMap<String>)session.getAttribute(J_POST);
+                if (jUri != null && jPost != null) {
+                    StringBuffer buf = request.getRequestURL();
+                    if (request.getQueryString() != null) {
+                        buf.append("?").append(request.getQueryString());
+                    }
+
+                    if (jUri.equals(buf.toString())) {
+                        // This is a retry of an original POST request
+                        // so restore method and parameters
+
+                        session.removeAttribute(J_POST);            
+                        Request baseRequest = (Request)request; 
+                        // (req instanceof Request)?(Request)
+                        // req:HttpConnection.getCurrentConnection().getRequest();
+                        baseRequest.setMethod(HttpMethod.POST.asString());
+                        baseRequest.setQueryParameters(jPost);
+                    }
+                } else if (jUri != null) {
+                    session.removeAttribute(J_URI);
+                }
+                        
+                return authentication;
+            }
+        }
+        return null;
+    }
+    
     private boolean isTokenExpired(FedizContext fedConfig, UserIdentity userIdentity) {
         if (fedConfig.isDetectExpiredTokens()) {
             try {
@@ -465,7 +480,8 @@ public class FederationAuthenticator extends LoginAuthenticator {
      *             {@link HttpServletResponse#sendError(int, String)} throws an
      *             {@link IOException}
      */
-    protected void signInRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor, HttpSession session)
+    protected void signInRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, 
+                                          FedizProcessor processor, HttpSession session)
         throws IOException {
 
         //Not supported in jetty 7.6
@@ -486,7 +502,7 @@ public class FederationAuthenticator extends LoginAuthenticator {
                     }
                 }
                 
-                synchronized(session) {
+                synchronized (session) {
                     session.setAttribute(J_CONTEXT, redirectionResponse.getRequestState().getState());
                 }
                 
@@ -504,7 +520,8 @@ public class FederationAuthenticator extends LoginAuthenticator {
         
     }
 
-    protected void signOutRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, FedizProcessor processor)
+    protected void signOutRedirectToIssuer(HttpServletRequest request, HttpServletResponse response, 
+                                           FedizProcessor processor)
             throws IOException {
 
         //Not supported in jetty 7.6


[2/2] cxf-fediz git commit: FEDIZ-186 - Add new logoutRedirectToConstraint plugin configuration parameter

Posted by co...@apache.org.
FEDIZ-186 - Add new logoutRedirectToConstraint plugin configuration parameter


Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/e8889b43
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/e8889b43
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/e8889b43

Branch: refs/heads/master
Commit: e8889b4385348ff71bab8cebbd7ea70e68ea13d2
Parents: db80f26
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Dec 21 14:54:00 2016 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Dec 21 14:54:00 2016 +0000

----------------------------------------------------------------------
 .../cxf/fediz/core/config/FedizContext.java     |   8 +
 .../cxf/fediz/core/handler/LogoutHandler.java   |  31 +-
 .../core/processor/FederationProcessorImpl.java |  43 ++-
 .../src/main/resources/schemas/FedizConfig.xsd  |  11 +
 .../core/federation/FederationLogoutTest.java   | 357 +++++++++++++++++++
 .../test/resources/fediz_test_config_logout.xml |  88 +++++
 6 files changed, 520 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FedizContext.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FedizContext.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FedizContext.java
index 537b137..3a329e1 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FedizContext.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FedizContext.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
+import java.util.regex.Pattern;
 
 import org.apache.cxf.fediz.core.config.jaxb.CertificateStores;
 import org.apache.cxf.fediz.core.config.jaxb.ContextConfig;
@@ -69,6 +70,7 @@ public class FedizContext implements Closeable {
     private KeyManager keyManager;
     private KeyManager decryptionKeyManager;
     private ClassLoader classloader;
+    private Pattern logoutRedirectToConstraint;
     
 
     public FedizContext(ContextConfig config) {
@@ -170,6 +172,12 @@ public class FedizContext implements Closeable {
         return config.getLogoutRedirectTo();
     }
     
+    public Pattern getLogoutRedirectToConstraint() {
+        if (logoutRedirectToConstraint == null && config.getLogoutRedirectToConstraint() != null) {
+            logoutRedirectToConstraint = Pattern.compile(config.getLogoutRedirectToConstraint());
+        }
+        return logoutRedirectToConstraint;
+    }
     
     public KeyManager getSigningKey() {
         

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
index 79d6fce..b547b00 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/handler/LogoutHandler.java
@@ -23,6 +23,8 @@ import java.io.InputStream;
 import java.net.URLEncoder;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
@@ -88,18 +90,31 @@ public class LogoutHandler implements RequestHandler<Boolean> {
     protected boolean signoutCleanup(HttpServletRequest request, HttpServletResponse response) {
         LOG.info("SignOutCleanup request found. Terminating user session.");
         request.getSession().invalidate();
+        
         String wreply = request.getParameter(FederationConstants.PARAM_REPLY);
+        Pattern logoutRedirectToConstraint = fedizConfig.getLogoutRedirectToConstraint();
+        
         if (wreply != null && !wreply.isEmpty()) {
-            try {
-                LOG.debug("Redirecting user after logout to: {}", wreply);
-                response.sendRedirect(URLEncoder.encode(wreply, "UTF-8"));
-            } catch (IOException e) {
-                LOG.error("Error redirecting user after logout: {}", e.getMessage());
+            if (logoutRedirectToConstraint == null) {
+                LOG.debug("No regular expression constraint configured for logout. Ignoring wreply parameter");
+            } else {
+                Matcher matcher = logoutRedirectToConstraint.matcher(wreply);
+                if (matcher.matches()) {
+                    try {
+                        LOG.debug("Redirecting user after logout to: {}", wreply);
+                        response.sendRedirect(URLEncoder.encode(wreply, "UTF-8"));
+                        return true;
+                    } catch (IOException e) {
+                        LOG.error("Error redirecting user after logout: {}", e.getMessage());
+                    }
+                } else {
+                    LOG.warn("The received wreply address {} does not match the configured constraint {}",
+                             wreply, logoutRedirectToConstraint);
+                }
             }
-        } else {
-            LOG.debug("No wreply parameter was set in logout action. Returning logout image");
-            writeLogoutImage(response);
         }
+        
+        writeLogoutImage(response);
         return true;
     }
 

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
index b82777b..5cb626e 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
@@ -34,6 +34,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -499,21 +501,42 @@ public class FederationProcessorImpl extends AbstractFedizProcessor {
             StringBuilder sb = new StringBuilder();
             sb.append(FederationConstants.PARAM_ACTION).append('=').append(FederationConstants.ACTION_SIGNOUT);
 
-            String logoutRedirectTo = request.getParameter(FederationConstants.PARAM_REPLY);
-            if (logoutRedirectTo == null || logoutRedirectTo.isEmpty()) {
-                logoutRedirectTo = config.getLogoutRedirectTo();
-            }
-            if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) {
-
-                if (logoutRedirectTo.startsWith("/")) {
-                    logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo.substring(1));
+            // Match the 'wreply' parameter against the constraint
+            String logoutRedirectTo = null;
+            Pattern logoutRedirectToConstraint = config.getLogoutRedirectToConstraint();
+            if (request.getParameter(FederationConstants.PARAM_REPLY) != null) {
+                if (logoutRedirectToConstraint == null) {
+                    LOG.debug("No regular expression constraint configured for logout. Ignoring wreply parameter");
                 } else {
-                    logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo);
+                    Matcher matcher = 
+                        logoutRedirectToConstraint.matcher(request.getParameter(FederationConstants.PARAM_REPLY));
+                    if (matcher.matches()) {
+                        logoutRedirectTo = request.getParameter(FederationConstants.PARAM_REPLY);
+                    } else {
+                        LOG.warn("The received wreply address {} does not match the configured constraint {}",
+                                 logoutRedirectTo, logoutRedirectToConstraint);
+                    }
                 }
-
+            }
+            
+            if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) {
                 LOG.debug("wreply={}", logoutRedirectTo);
                 sb.append('&').append(FederationConstants.PARAM_REPLY).append('=');
                 sb.append(URLEncoder.encode(logoutRedirectTo, "UTF-8"));
+            } else {
+                logoutRedirectTo = config.getLogoutRedirectTo();
+                if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) {
+    
+                    if (logoutRedirectTo.startsWith("/")) {
+                        logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo.substring(1));
+                    } else {
+                        logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo);
+                    }
+    
+                    LOG.debug("wreply={}", logoutRedirectTo);
+                    sb.append('&').append(FederationConstants.PARAM_REPLY).append('=');
+                    sb.append(URLEncoder.encode(logoutRedirectTo, "UTF-8"));
+                }
             }
 
             redirectURL = redirectURL + "?" + sb.toString();

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/main/resources/schemas/FedizConfig.xsd
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/resources/schemas/FedizConfig.xsd b/plugins/core/src/main/resources/schemas/FedizConfig.xsd
index b556e8b..47b3a98 100644
--- a/plugins/core/src/main/resources/schemas/FedizConfig.xsd
+++ b/plugins/core/src/main/resources/schemas/FedizConfig.xsd
@@ -27,6 +27,7 @@
                 <xs:element ref="protocol" />
                 <xs:element ref="logoutURL" minOccurs="0" />
                 <xs:element ref="logoutRedirectTo" minOccurs="0" />
+                <xs:element ref="logoutRedirectToConstraint" minOccurs="0" />
             </xs:sequence>
             <xs:attribute name="name" use="required" type="xs:string" />
 
@@ -143,6 +144,16 @@
             </xs:documentation>
         </xs:annotation>
     </xs:element>
+    
+    <xs:element name="logoutRedirectToConstraint" type="xs:string">
+        <xs:annotation>
+            <xs:documentation>A regular expression constraint on the 'wreply' parameter, which is used to obtain the URL to 
+                navigate to after successful logout. If the constraint is not specified, then the 'wreply' parameter is ignored 
+                and instead the URL is taken from the "logoutRedirectTo" configuration option.
+                Example: 'https://localhost:12345/logout.*/'
+            </xs:documentation>
+        </xs:annotation>
+    </xs:element>
 
     <xs:complexType name="federationProtocolType">
         <xs:complexContent>

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
new file mode 100644
index 0000000..dedc9f4
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/FederationLogoutTest.java
@@ -0,0 +1,357 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core.federation;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLEncoder;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.cxf.fediz.common.SecurityTestUtil;
+import org.apache.cxf.fediz.core.FederationConstants;
+import org.apache.cxf.fediz.core.config.FedizConfigurator;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.handler.LogoutHandler;
+import org.easymock.EasyMock;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+
+/**
+ * Some tests for logout for WS-Federation
+ */
+public class FederationLogoutTest {
+    private static final String LOGOUT_URL = "https://localhost/fedizhelloworld/secure/logout";
+    private static final String LOGOUT_URI = "/secure/logout";
+    private static final String REPLY_URL = "https://localhost/fedizhelloworld/secure/wreply.html";
+    private static final String BAD_REPLY_URL = "https://localhost/fedizhelloworld/secure/badreply.html";
+    
+    private static final String CONFIG_FILE = "fediz_test_config_logout.xml";
+    
+    private static FedizConfigurator configurator;
+    private static DocumentBuilderFactory docBuilderFactory;
+    
+    static {
+        docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+    }
+    
+    
+    @BeforeClass
+    public static void init() {
+        getFederationConfigurator();
+        Assert.assertNotNull(configurator);
+    }
+    
+    @AfterClass
+    public static void cleanup() {
+        SecurityTestUtil.cleanup();
+    }
+    
+
+    private static FedizConfigurator getFederationConfigurator() {
+        if (configurator != null) {
+            return configurator;
+        }
+        try {
+            configurator = new FedizConfigurator();
+            final URL resource = Thread.currentThread().getContextClassLoader()
+                    .getResource(CONFIG_FILE);
+            File f = new File(resource.toURI());
+            configurator.loadConfig(f);
+            return configurator;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    
+    @org.junit.Test
+    public void testSignoutCustomURL() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Flogout%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCustomURLWithWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=" + URLEncoder.encode(REPLY_URL, "UTF-8");
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCustomURLWithBadWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Flogout%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCustomURLWithNoConfiguredConstraint() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT2");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION)).andReturn(null).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(LOGOUT_URL));
+        EasyMock.expect(req.getRequestURI()).andReturn(LOGOUT_URI);
+        EasyMock.expect(req.getContextPath()).andReturn(LOGOUT_URI);
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Flogout%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutAction() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(null);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
+        EasyMock.expect(req.getRequestURI()).andReturn("/secure");
+        EasyMock.expect(req.getContextPath()).andReturn("/secure");
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutActionWithWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
+        EasyMock.expect(req.getRequestURI()).andReturn("/secure");
+        EasyMock.expect(req.getContextPath()).andReturn("/secure");
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=" + URLEncoder.encode(REPLY_URL, "UTF-8");
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutActionWithBadWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
+        EasyMock.expect(req.getRequestURI()).andReturn("/secure");
+        EasyMock.expect(req.getContextPath()).andReturn("/secure");
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutActionWithNoConfiguredConstraint() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT2");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+        .andReturn(FederationConstants.ACTION_SIGNOUT).anyTimes();
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer("https://localhost/fedizhelloworld/secure"));
+        EasyMock.expect(req.getRequestURI()).andReturn("/secure");
+        EasyMock.expect(req.getContextPath()).andReturn("/secure");
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirectToIdP = 
+            "http://url_to_the_issuer?wa=wsignout1.0&wreply=https%3A%2F%2Flocalhost%2Fsecure%2Findex.html";
+        resp.sendRedirect(expectedRedirectToIdP);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCleanupWithWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        HttpSession session =  EasyMock.createMock(HttpSession.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
+        EasyMock.expect(req.getSession()).andReturn(session);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        String expectedRedirect = URLEncoder.encode(REPLY_URL, "UTF-8");
+        resp.sendRedirect(expectedRedirect);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCleanupWithBadWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        HttpSession session =  EasyMock.createMock(HttpSession.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
+        EasyMock.expect(req.getSession()).andReturn(session);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(BAD_REPLY_URL).anyTimes();
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        resp.setContentType("image/jpeg");
+        ServletOutputStream outputStream = EasyMock.createMock(ServletOutputStream.class);
+        EasyMock.expect(resp.getOutputStream()).andReturn(outputStream);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+    
+    @org.junit.Test
+    public void testSignoutCleanupWithNoConfiguredConstraint() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT2");
+        
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        HttpSession session =  EasyMock.createMock(HttpSession.class);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_ACTION))
+            .andReturn(FederationConstants.ACTION_SIGNOUT_CLEANUP).anyTimes();
+        EasyMock.expect(req.getSession()).andReturn(session);
+        EasyMock.expect(req.getParameter(FederationConstants.PARAM_REPLY)).andReturn(REPLY_URL).anyTimes();
+        EasyMock.replay(req);
+        
+        LogoutHandler logoutHandler = new LogoutHandler(config);
+        Assert.assertTrue(logoutHandler.canHandleRequest(req));
+        
+        HttpServletResponse resp = EasyMock.createMock(HttpServletResponse.class);
+        resp.setContentType("image/jpeg");
+        ServletOutputStream outputStream = EasyMock.createMock(ServletOutputStream.class);
+        EasyMock.expect(resp.getOutputStream()).andReturn(outputStream);
+        EasyMock.expectLastCall();
+        EasyMock.replay(resp);
+        logoutHandler.handleRequest(req, resp);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/e8889b43/plugins/core/src/test/resources/fediz_test_config_logout.xml
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/resources/fediz_test_config_logout.xml b/plugins/core/src/test/resources/fediz_test_config_logout.xml
new file mode 100644
index 0000000..030281e
--- /dev/null
+++ b/plugins/core/src/test/resources/fediz_test_config_logout.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+ 
+  http://www.apache.org/licenses/LICENSE-2.0
+ 
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<FedizConfig>
+	<contextConfig name="ROOT">
+		<audienceUris>
+			<audienceItem>http://host_one:port/url</audienceItem>
+		</audienceUris>
+		<certificateStores>
+			<trustManager>
+				<keyStore file="ststrust.jks" password="storepass"
+					type="JKS" />
+			</trustManager>		
+		</certificateStores>
+		<trustedIssuers>
+			<issuer certificateValidation="PeerTrust" />
+		</trustedIssuers>
+
+		<maximumClockSkew>1000</maximumClockSkew>
+		<protocol xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+			xsi:type="federationProtocolType" version="1.2">
+			<realm>target realm</realm>
+			<issuer>http://url_to_the_issuer</issuer>
+			<roleDelimiter>;</roleDelimiter>
+			<roleURI>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role</roleURI>
+			<authenticationType value="some auth type" type="String" />
+			<freshness>10000</freshness>
+			<reply>reply value</reply>
+			<request>REQUEST</request>
+			<claimTypesRequested>
+				<claimType type="a particular claim type" optional="true" />
+			</claimTypesRequested>
+		</protocol>
+		<logoutURL>secure/logout</logoutURL>
+        <logoutRedirectTo>/index.html</logoutRedirectTo>
+        <logoutRedirectToConstraint>.*wreply.html</logoutRedirectToConstraint>
+	</contextConfig>
+	
+	<contextConfig name="ROOT2">
+		<audienceUris>
+			<audienceItem>http://host_one:port/url</audienceItem>
+		</audienceUris>
+		<certificateStores>
+			<trustManager>
+				<keyStore file="ststrust.jks" password="storepass"
+					type="JKS" />
+			</trustManager>		
+		</certificateStores>
+		<trustedIssuers>
+			<issuer certificateValidation="PeerTrust" />
+		</trustedIssuers>
+
+		<maximumClockSkew>1000</maximumClockSkew>
+		<protocol xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+			xsi:type="federationProtocolType" version="1.2">
+			<realm>target realm</realm>
+			<issuer>http://url_to_the_issuer</issuer>
+			<roleDelimiter>;</roleDelimiter>
+			<roleURI>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role</roleURI>
+			<authenticationType value="some auth type" type="String" />
+			<freshness>10000</freshness>
+			<reply>reply value</reply>
+			<request>REQUEST</request>
+			<claimTypesRequested>
+				<claimType type="a particular claim type" optional="true" />
+			</claimTypesRequested>
+		</protocol>
+		<logoutURL>secure/logout</logoutURL>
+        <logoutRedirectTo>/index.html</logoutRedirectTo>
+	</contextConfig>
+	
+</FedizConfig>