Hi,

Can one of the developers take a look at the attached patch? It adds a
"X-CAS-Action: logout" header to the SSO request. Applies cleanly to
(at least) 3.4.2.1 and 3.4.3.1.

Background: mod_auth_cas cannot reliably support SSO due to how
request body filters are implemented in apache2. Currently, if you
turn on SSO, mod_auth_cas also eats regular POST requests. This patch
would make that trivially easy to fix.

Thanks!

Ben Noordhuis
NRC Media

-- 
You are currently subscribed to [email protected] as: 
[email protected]
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/cas-dev
From 5b792c3903bcedeb08a6536d50595fed49b57571 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis <[email protected]>
Date: Thu, 2 Dec 2010 14:15:08 +0100
Subject: [PATCH] Send a X-CAS-Action header with the SSO request so the client (e.g. mod_auth_cas) can easily recognize it.

---
 .../principal/AbstractWebApplicationService.java   |   51 ++++++++++---------
 .../main/java/org/jasig/cas/util/HttpClient.java   |   42 +++++++++++-----
 2 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/cas-server-core/src/main/java/org/jasig/cas/authentication/principal/AbstractWebApplicationService.java b/cas-server-core/src/main/java/org/jasig/cas/authentication/principal/AbstractWebApplicationService.java
index b826655..e5ffaa9 100644
--- a/cas-server-core/src/main/java/org/jasig/cas/authentication/principal/AbstractWebApplicationService.java
+++ b/cas-server-core/src/main/java/org/jasig/cas/authentication/principal/AbstractWebApplicationService.java
@@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Abstract implementation of a WebApplicationService.
- * 
+ *
  * @author Scott Battaglia
  * @version $Revision: 1.3 $ $Date: 2007/04/19 20:13:01 $
  * @since 3.1
@@ -27,40 +27,41 @@ import org.slf4j.LoggerFactory;
 public abstract class AbstractWebApplicationService implements WebApplicationService {
 
     protected static final Logger LOG = LoggerFactory.getLogger(SamlService.class);
-    
+
     private static final Map<String, Object> EMPTY_MAP = Collections.unmodifiableMap(new HashMap<String, Object>());
-    
+
     private static final UniqueTicketIdGenerator GENERATOR = new DefaultUniqueTicketIdGenerator();
-    
+
     /** The id of the service. */
     private final String id;
-    
+
     /** The original url provided, used to reconstruct the redirect url. */
     private final String originalUrl;
 
     private final String artifactId;
-    
+
     private Principal principal;
-    
+
     private boolean loggedOutAlready = false;
-    
+
     private final HttpClient httpClient;
-    
+
     protected AbstractWebApplicationService(final String id, final String originalUrl, final String artifactId, final HttpClient httpClient) {
         this.id = id;
         this.originalUrl = originalUrl;
         this.artifactId = artifactId;
         this.httpClient = httpClient;
     }
-    
-    public final String toString() {
+
+    @Override
+	public final String toString() {
         return this.id;
     }
-    
+
     public final String getId() {
         return this.id;
     }
-    
+
     public final String getArtifactId() {
         return this.artifactId;
     }
@@ -89,12 +90,13 @@ public abstract class AbstractWebApplicationService implements WebApplicationSer
         return url.substring(0, jsessionPosition)
             + url.substring(questionMarkPosition);
     }
-    
+
     protected final String getOriginalUrl() {
         return this.originalUrl;
     }
 
-    public boolean equals(final Object object) {
+    @Override
+	public boolean equals(final Object object) {
         if (object == null) {
             return false;
         }
@@ -107,15 +109,16 @@ public abstract class AbstractWebApplicationService implements WebApplicationSer
 
         return false;
     }
-    
-    public int hashCode() {
+
+    @Override
+	public int hashCode() {
         final int prime = 41;
         int result = 1;
         result = prime * result
             + ((this.id == null) ? 0 : this.id.hashCode());
         return result;
     }
-    
+
     protected Principal getPrincipal() {
         return this.principal;
     }
@@ -123,11 +126,11 @@ public abstract class AbstractWebApplicationService implements WebApplicationSer
     public void setPrincipal(final Principal principal) {
         this.principal = principal;
     }
-    
+
     public boolean matches(final Service service) {
         return this.id.equals(service.getId());
     }
-    
+
     public synchronized boolean logOutOfService(final String sessionIdentifier) {
         if (this.loggedOutAlready) {
             return true;
@@ -140,13 +143,13 @@ public abstract class AbstractWebApplicationService implements WebApplicationSer
             + "\" Version=\"2.0\" IssueInstant=\"" + SamlUtils.getCurrentDateAndTime()
             + "\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@</saml:NameID><samlp:SessionIndex>"
             + sessionIdentifier + "</samlp:SessionIndex></samlp:LogoutRequest>";
-        
+
         this.loggedOutAlready = true;
-        
+
         if (this.httpClient != null) {
-            return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest, true);
+            return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), "logout", logoutRequest, true);
         }
-        
+
         return false;
     }
 }
diff --git a/cas-server-core/src/main/java/org/jasig/cas/util/HttpClient.java b/cas-server-core/src/main/java/org/jasig/cas/util/HttpClient.java
index daaa054..d9d661f 100644
--- a/cas-server-core/src/main/java/org/jasig/cas/util/HttpClient.java
+++ b/cas-server-core/src/main/java/org/jasig/cas/util/HttpClient.java
@@ -5,25 +5,30 @@
  */
 package org.jasig.cas.util;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.util.Assert;
-import org.springframework.beans.factory.DisposableBean;
-
-import javax.validation.constraints.Min;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
 import java.io.BufferedReader;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Serializable;
-import java.net.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.util.Assert;
+
 /**
  * @author Scott Battaglia
  * @version $Revision: 20191 $ $Date: 2010-03-14 00:05:58 -0500 (Sun, 14 Mar 2010) $
@@ -73,12 +78,13 @@ public final class HttpClient implements Serializable, DisposableBean {
      * This is useful when it doesn't matter about the response as you'll perform no action based on the response.
      *
      * @param url the url to send the message to
+     * @param action the value of the X-CAS-Action header, null to send no header.
      * @param message the message itself
      * @param async true if you don't want to wait for the response, false otherwise.
      * @return boolean if the message was sent, or async was used.  false if the message failed.
      */
-    public boolean sendMessageToEndPoint(final String url, final String message, final boolean async) {
-        final Future<Boolean> result = EXECUTOR_SERVICE.submit(new MessageSender(url, message, this.readTimeout, this.connectionTimeout));
+    public boolean sendMessageToEndPoint(final String url, final String action, final String message, final boolean async) {
+        final Future<Boolean> result = EXECUTOR_SERVICE.submit(new MessageSender(url, action, message, this.readTimeout, this.connectionTimeout));
 
         if (async) {
             return true;
@@ -137,7 +143,7 @@ public final class HttpClient implements Serializable, DisposableBean {
     /**
      * Set the acceptable HTTP status codes that we will use to determine if the
      * response from the URL was correct.
-     * 
+     *
      * @param acceptableCodes an array of status code integers.
      */
     public final void setAcceptableCodes(final int[] acceptableCodes) {
@@ -160,14 +166,17 @@ public final class HttpClient implements Serializable, DisposableBean {
 
         private String url;
 
+        private String action;
+
         private String message;
 
         private int readTimeout;
 
         private int connectionTimeout;
 
-        public MessageSender(final String url, final String message, final int readTimeout, final int connectionTimeout) {
+        public MessageSender(final String url, final String action, final String message, final int readTimeout, final int connectionTimeout) {
             this.url = url;
+            this.action = action;
             this.message = message;
             this.readTimeout = readTimeout;
             this.connectionTimeout = connectionTimeout;
@@ -191,6 +200,11 @@ public final class HttpClient implements Serializable, DisposableBean {
                 connection.setConnectTimeout(connectionTimeout);
                 connection.setRequestProperty("Content-Length", Integer.toString(output.getBytes().length));
                 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+                if (action != null) {
+                    connection.setRequestProperty("X-CAS-Action", action);
+                }
+
                 final DataOutputStream printout = new DataOutputStream(connection.getOutputStream());
                 printout.writeBytes(output);
                 printout.flush();
-- 
1.7.0.4

Reply via email to