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 2017/02/13 16:54:33 UTC

cxf-fediz git commit: FEDIZ-190 - Make the logoutRedirectToConstraint a CallbackType

Repository: cxf-fediz
Updated Branches:
  refs/heads/master d18087dd4 -> 7bd84d3a8


FEDIZ-190 - Make the logoutRedirectToConstraint a CallbackType


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

Branch: refs/heads/master
Commit: 7bd84d3a8e276b40bd401c2125f3477204cfb3d8
Parents: d18087d
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Mon Feb 13 16:54:23 2017 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Mon Feb 13 16:54:23 2017 +0000

----------------------------------------------------------------------
 .../cxf/fediz/core/config/ConfigUtils.java      | 71 ++++++++++++++++++++
 .../fediz/core/config/FederationProtocol.java   | 14 ++--
 .../cxf/fediz/core/config/FedizContext.java     | 17 +++--
 .../apache/cxf/fediz/core/config/Protocol.java  | 38 +----------
 .../cxf/fediz/core/handler/LogoutHandler.java   | 30 ++++++++-
 .../core/processor/FederationProcessorImpl.java | 24 ++++++-
 .../fediz/core/spi/ReplyConstraintCallback.java | 42 ++++++++++++
 .../src/main/resources/schemas/FedizConfig.xsd  | 20 +++---
 .../core/federation/FederationLogoutTest.java   | 48 +++++++++++++
 .../LogoutRedirectConstraintHandler.java        | 46 +++++++++++++
 .../test/resources/fediz_test_config_logout.xml | 35 ++++++++++
 11 files changed, 325 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/ConfigUtils.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/ConfigUtils.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/ConfigUtils.java
new file mode 100644
index 0000000..70b450a
--- /dev/null
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/ConfigUtils.java
@@ -0,0 +1,71 @@
+/**
+ * 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.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cxf.fediz.core.config.jaxb.ArgumentType;
+import org.apache.cxf.fediz.core.config.jaxb.CallbackType;
+import org.apache.cxf.fediz.core.util.ClassLoaderUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ConfigUtils {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigUtils.class);
+    
+    private ConfigUtils() {
+        // complete
+    }
+    
+    public static Object loadCallbackType(CallbackType cbt, String name, ClassLoader classLoader) {
+        if (cbt == null || cbt.getValue() == null) {
+            return null;
+        }
+        if (cbt.getType() == null || cbt.getType().equals(ArgumentType.STRING)) {
+            return cbt.getValue();
+        } else if (cbt.getType().equals(ArgumentType.CLASS)) {
+            List<Object> handler = new ArrayList<>();
+            String[] cbtHandler = cbt.getValue().split(",");
+            for (String cbh : cbtHandler) {
+                try {
+                    if (classLoader == null) {
+                        handler.add(ClassLoaderUtils.loadClass(cbh, ConfigUtils.class).newInstance());
+                    } else {
+                        handler.add(classLoader.loadClass(cbh).newInstance());
+                    }
+                } catch (Exception e) {
+                    LOG.error("Failed to create instance of " + cbh, e);
+                    //throw new IllegalStateException("Failed to create instance of " + cbt.getValue());
+                }
+            }
+            if (handler.size() == 1) {
+                // Backward compatible return handler directly if only one is configured
+                return handler.get(0);
+            } else {
+                return handler;
+            }
+        } else {
+            LOG.error("Only String and Class are supported for '{}'", name);
+            throw new IllegalStateException("Only String and Class are supported for '" + name + "'");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FederationProtocol.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FederationProtocol.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FederationProtocol.java
index a3a87dd..41d56f8 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FederationProtocol.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/FederationProtocol.java
@@ -63,7 +63,7 @@ public class FederationProtocol extends Protocol {
             return this.authenticationType;
         }
         CallbackType cbt = getFederationProtocol().getAuthenticationType();
-        this.authenticationType = loadCallbackType(cbt, "AuthenticationType");
+        this.authenticationType = ConfigUtils.loadCallbackType(cbt, "AuthenticationType", getClassloader());
         return this.authenticationType;
     }
 
@@ -84,7 +84,7 @@ public class FederationProtocol extends Protocol {
             return this.homeRealm;
         }
         CallbackType cbt = getFederationProtocol().getHomeRealm();
-        this.homeRealm = loadCallbackType(cbt, "HomeRealm");
+        this.homeRealm = ConfigUtils.loadCallbackType(cbt, "HomeRealm", getClassloader());
         return this.homeRealm;
     }
 
@@ -105,7 +105,7 @@ public class FederationProtocol extends Protocol {
             return this.freshness;
         }
         CallbackType cbt = getFederationProtocol().getFreshness();
-        this.freshness = loadCallbackType(cbt, "Freshness");
+        this.freshness = ConfigUtils.loadCallbackType(cbt, "Freshness", getClassloader());
         return this.freshness;
     }
 
@@ -126,7 +126,7 @@ public class FederationProtocol extends Protocol {
             return this.signInQuery;
         }
         CallbackType cbt = getFederationProtocol().getSignInQuery();
-        this.signInQuery = loadCallbackType(cbt, "SignInQuery");
+        this.signInQuery = ConfigUtils.loadCallbackType(cbt, "SignInQuery", getClassloader());
         return this.signInQuery;
     }
 
@@ -147,7 +147,7 @@ public class FederationProtocol extends Protocol {
             return this.signOutQuery;
         }
         CallbackType cbt = getFederationProtocol().getSignOutQuery();
-        this.signOutQuery = loadCallbackType(cbt, "SignOutQuery");
+        this.signOutQuery = ConfigUtils.loadCallbackType(cbt, "SignOutQuery", getClassloader());
         return this.signOutQuery;
     }
 
@@ -168,7 +168,7 @@ public class FederationProtocol extends Protocol {
             return this.request;
         }
         CallbackType cbt = getFederationProtocol().getRequest();
-        this.request = loadCallbackType(cbt, "Request");
+        this.request = ConfigUtils.loadCallbackType(cbt, "Request", getClassloader());
         return this.request;
     }
 
@@ -189,7 +189,7 @@ public class FederationProtocol extends Protocol {
             return this.reply;
         }
         CallbackType cbt = getFederationProtocol().getReply();
-        this.reply = loadCallbackType(cbt, "Reply");
+        this.reply = ConfigUtils.loadCallbackType(cbt, "Reply", getClassloader());
         return this.reply;
     }
 

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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 c3111de..17014c0 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
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.regex.Pattern;
 
+import org.apache.cxf.fediz.core.config.jaxb.CallbackType;
 import org.apache.cxf.fediz.core.config.jaxb.CertificateStores;
 import org.apache.cxf.fediz.core.config.jaxb.ContextConfig;
 import org.apache.cxf.fediz.core.config.jaxb.FederationProtocolType;
@@ -70,7 +71,7 @@ public class FedizContext implements Closeable {
     private KeyManager keyManager;
     private KeyManager decryptionKeyManager;
     private ClassLoader classloader;
-    private Pattern logoutRedirectToConstraint;
+    private Object logoutRedirectToConstraint;
 
 
     public FedizContext(ContextConfig config) {
@@ -172,9 +173,17 @@ public class FedizContext implements Closeable {
         return config.getLogoutRedirectTo();
     }
 
-    public Pattern getLogoutRedirectToConstraint() {
-        if (logoutRedirectToConstraint == null && config.getLogoutRedirectToConstraint() != null) {
-            logoutRedirectToConstraint = Pattern.compile(config.getLogoutRedirectToConstraint());
+    public Object getLogoutRedirectToConstraint() {
+        if (logoutRedirectToConstraint != null) {
+            return logoutRedirectToConstraint;
+        }
+        
+        CallbackType cbt = config.getLogoutRedirectToConstraint();
+        Object constraint = ConfigUtils.loadCallbackType(cbt, "LogoutRedirectToConstraint", getClassloader());
+        if (constraint instanceof String) {
+            logoutRedirectToConstraint = Pattern.compile((String)constraint);
+        } else {
+            this.logoutRedirectToConstraint = constraint;
         }
         return logoutRedirectToConstraint;
     }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/Protocol.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/Protocol.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/Protocol.java
index f21b8a1..ed52873 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/Protocol.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/Protocol.java
@@ -25,7 +25,6 @@ import java.util.List;
 import javax.security.auth.callback.CallbackHandler;
 
 import org.apache.cxf.fediz.core.TokenValidator;
-import org.apache.cxf.fediz.core.config.jaxb.ArgumentType;
 import org.apache.cxf.fediz.core.config.jaxb.CallbackType;
 import org.apache.cxf.fediz.core.config.jaxb.ClaimType;
 import org.apache.cxf.fediz.core.config.jaxb.ClaimTypesRequested;
@@ -133,7 +132,7 @@ public abstract class Protocol {
             return this.issuer;
         }
         CallbackType cbt = getProtocolType().getIssuer();
-        this.issuer = loadCallbackType(cbt, "Issuer");
+        this.issuer = ConfigUtils.loadCallbackType(cbt, "Issuer", getClassloader());
         return this.issuer;
     }
 
@@ -154,7 +153,7 @@ public abstract class Protocol {
             return this.realm;
         }
         CallbackType cbt = getProtocolType().getRealm();
-        this.realm = loadCallbackType(cbt, "Realm");
+        this.realm = ConfigUtils.loadCallbackType(cbt, "Realm", getClassloader());
         return this.realm;
     }
 
@@ -174,39 +173,6 @@ public abstract class Protocol {
         return validators;
     }
 
-    protected Object loadCallbackType(CallbackType cbt, String name) {
-        if (cbt == null || cbt.getValue() == null) {
-            return null;
-        }
-        if (cbt.getType() == null || cbt.getType().equals(ArgumentType.STRING)) {
-            return cbt.getValue();
-        } else if (cbt.getType().equals(ArgumentType.CLASS)) {
-            List<Object> handler = new ArrayList<>();
-            String[] cbtHandler = cbt.getValue().split(",");
-            for (String cbh : cbtHandler) {
-                try {
-                    if (getClassloader() == null) {
-                        handler.add(ClassLoaderUtils.loadClass(cbh, this.getClass()).newInstance());
-                    } else {
-                        handler.add(getClassloader().loadClass(cbh).newInstance());
-                    }
-                } catch (Exception e) {
-                    LOG.error("Failed to create instance of " + cbh, e);
-                    //throw new IllegalStateException("Failed to create instance of " + cbt.getValue());
-                }
-            }
-            if (handler.size() == 1) {
-                // Backward compatible return handler directly if only one is configured
-                return handler.get(0);
-            } else {
-                return handler;
-            }
-        } else {
-            LOG.error("Only String and Class are supported for '{}'", name);
-            throw new IllegalStateException("Only String and Class are supported for '" + name + "'");
-        }
-    }
-
     public List<Claim> getClaimTypesRequested() {
         ClaimTypesRequested claimsRequested = getProtocolType().getClaimTypesRequested();
         List<Claim> claims = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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 59fc613..4e72863 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
@@ -26,6 +26,9 @@ import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -37,6 +40,7 @@ import org.apache.cxf.fediz.core.config.FedizContext;
 import org.apache.cxf.fediz.core.processor.FedizProcessor;
 import org.apache.cxf.fediz.core.processor.FedizProcessorFactory;
 import org.apache.cxf.fediz.core.processor.RedirectionResponse;
+import org.apache.cxf.fediz.core.spi.ReplyConstraintCallback;
 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,9 +96,14 @@ public class LogoutHandler implements RequestHandler<Boolean> {
         request.getSession().invalidate();
 
         String wreply = request.getParameter(FederationConstants.PARAM_REPLY);
-        Pattern logoutRedirectToConstraint = fedizConfig.getLogoutRedirectToConstraint();
 
         if (wreply != null && !wreply.isEmpty()) {
+            Pattern logoutRedirectToConstraint = null;
+            try {
+                logoutRedirectToConstraint = resolveLogoutRedirectToConstraint(request, fedizConfig);
+            } catch (Exception 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 {
@@ -117,6 +126,25 @@ public class LogoutHandler implements RequestHandler<Boolean> {
         writeLogoutImage(response);
         return true;
     }
+    
+    private Pattern resolveLogoutRedirectToConstraint(HttpServletRequest request, FedizContext config) 
+        throws IOException, UnsupportedCallbackException {
+        Object logoutConstraintObj = config.getLogoutRedirectToConstraint();
+        Pattern logoutConstraint = null;
+        if (logoutConstraintObj != null) {
+            if (logoutConstraintObj instanceof Pattern) {
+                logoutConstraint = (Pattern)logoutConstraintObj;
+            } else if (logoutConstraintObj instanceof CallbackHandler) {
+                CallbackHandler frCB = (CallbackHandler)logoutConstraintObj;
+                ReplyConstraintCallback callback = new ReplyConstraintCallback(request);
+                frCB.handle(new Callback[] {
+                    callback
+                });
+                logoutConstraint = callback.getReplyConstraint();
+            }
+        }
+        return logoutConstraint;
+    }
 
     public void setToken(Element token) {
         this.token = token;

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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 9e72080..5c9ae88 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
@@ -58,6 +58,7 @@ import org.apache.cxf.fediz.core.metadata.MetadataWriter;
 import org.apache.cxf.fediz.core.spi.FreshnessCallback;
 import org.apache.cxf.fediz.core.spi.HomeRealmCallback;
 import org.apache.cxf.fediz.core.spi.ReplyCallback;
+import org.apache.cxf.fediz.core.spi.ReplyConstraintCallback;
 import org.apache.cxf.fediz.core.spi.SignInQueryCallback;
 import org.apache.cxf.fediz.core.spi.SignOutQueryCallback;
 import org.apache.cxf.fediz.core.spi.WAuthCallback;
@@ -505,8 +506,8 @@ public class FederationProcessorImpl extends AbstractFedizProcessor {
 
             // Match the 'wreply' parameter against the constraint
             String logoutRedirectTo = null;
-            Pattern logoutRedirectToConstraint = config.getLogoutRedirectToConstraint();
             if (request.getParameter(FederationConstants.PARAM_REPLY) != null) {
+                Pattern logoutRedirectToConstraint = resolveLogoutRedirectToConstraint(request, config);
                 if (logoutRedirectToConstraint == null) {
                     LOG.debug("No regular expression constraint configured for logout. Ignoring wreply parameter");
                 } else {
@@ -585,9 +586,28 @@ public class FederationProcessorImpl extends AbstractFedizProcessor {
         }
         return signInQuery;
     }
+    
+    private Pattern resolveLogoutRedirectToConstraint(HttpServletRequest request, FedizContext config) 
+        throws IOException, UnsupportedCallbackException {
+        Object logoutConstraintObj = config.getLogoutRedirectToConstraint();
+        Pattern logoutConstraint = null;
+        if (logoutConstraintObj != null) {
+            if (logoutConstraintObj instanceof Pattern) {
+                logoutConstraint = (Pattern)logoutConstraintObj;
+            } else if (logoutConstraintObj instanceof CallbackHandler) {
+                CallbackHandler frCB = (CallbackHandler)logoutConstraintObj;
+                ReplyConstraintCallback callback = new ReplyConstraintCallback(request);
+                frCB.handle(new Callback[] {
+                    callback
+                });
+                logoutConstraint = callback.getReplyConstraint();
+            }
+        }
+        return logoutConstraint;
+    }
 
     private String resolveSignOutQuery(HttpServletRequest request, FedizContext config) throws IOException,
-        UnsupportedCallbackException, UnsupportedEncodingException {
+        UnsupportedCallbackException {
         Object signOutQueryObj = ((FederationProtocol)config.getProtocol()).getSignOutQuery();
         String signOutQuery = null;
         if (signOutQueryObj != null) {

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/plugins/core/src/main/java/org/apache/cxf/fediz/core/spi/ReplyConstraintCallback.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/spi/ReplyConstraintCallback.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/spi/ReplyConstraintCallback.java
new file mode 100644
index 0000000..f07defd
--- /dev/null
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/spi/ReplyConstraintCallback.java
@@ -0,0 +1,42 @@
+/**
+ * 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.spi;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class ReplyConstraintCallback extends AbstractServletCallback {
+
+    private Pattern replyConstraint;
+
+    public ReplyConstraintCallback(HttpServletRequest request) {
+        super(request);
+    }
+
+    public Pattern getReplyConstraint() {
+        return replyConstraint;
+    }
+
+    public void setReplyConstraint(Pattern replyConstraint) {
+        this.replyConstraint = replyConstraint;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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 7d95384..f034c6c 100644
--- a/plugins/core/src/main/resources/schemas/FedizConfig.xsd
+++ b/plugins/core/src/main/resources/schemas/FedizConfig.xsd
@@ -145,16 +145,6 @@
         </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>
             <xs:extension base="protocolType">
@@ -219,6 +209,16 @@
             </xs:extension>
         </xs:simpleContent>
     </xs:complexType>
+    
+    <xs:element name="logoutRedirectToConstraint" type="CallbackType">
+        <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.*/'. Alternatively it can be specified in a CallbackHandler
+            </xs:documentation>
+        </xs:annotation>
+    </xs:element>
 
     <xs:element name="issuer" type="CallbackType" />
     <xs:element name="homeRealm" type="CallbackType" />

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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
index 886d991..13bc2ad 100644
--- 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
@@ -159,6 +159,54 @@ public class FederationLogoutTest {
         EasyMock.replay(resp);
         logoutHandler.handleRequest(req, resp);
     }
+    
+    @org.junit.Test
+    public void testSignoutValidatorWithWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT5");
+
+        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 testSignoutValidatorWithBadWReply() throws Exception {
+        FedizContext config = getFederationConfigurator().getFedizContext("ROOT5");
+
+        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 {

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/LogoutRedirectConstraintHandler.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/LogoutRedirectConstraintHandler.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/LogoutRedirectConstraintHandler.java
new file mode 100644
index 0000000..9355b79
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/LogoutRedirectConstraintHandler.java
@@ -0,0 +1,46 @@
+/**
+ * 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.IOException;
+import java.util.regex.Pattern;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.cxf.fediz.core.spi.ReplyConstraintCallback;
+
+public class LogoutRedirectConstraintHandler implements CallbackHandler {
+
+    @Override
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+        if (callbacks != null) {
+            for (Callback callback : callbacks) {
+                if (callback instanceof ReplyConstraintCallback) {
+                    ReplyConstraintCallback replyConstraintCallback = (ReplyConstraintCallback)callback;
+                    replyConstraintCallback.setReplyConstraint(Pattern.compile(".*wreply.html"));
+                }
+            }
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/7bd84d3a/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
index 0c7831e..76109fa 100644
--- a/plugins/core/src/test/resources/fediz_test_config_logout.xml
+++ b/plugins/core/src/test/resources/fediz_test_config_logout.xml
@@ -154,4 +154,39 @@
         <logoutRedirectToConstraint>.*wreply.html</logoutRedirectToConstraint>
 	</contextConfig>
 	
+	<contextConfig name="ROOT5">
+		<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 type="Class">org.apache.cxf.fediz.core.federation.LogoutRedirectConstraintHandler</logoutRedirectToConstraint>
+	</contextConfig>
+	
+	
 </FedizConfig>