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