support for Tomcat: logout, proxy callback.
This commit is contained in:
Scott Battaglia 2010-08-29 21:53:32 +00:00
parent 74a8cff651
commit 9ffca231a0
8 changed files with 287 additions and 18 deletions

View File

@ -217,12 +217,10 @@ public final class CommonUtils {
buffer.append(request.getRequestURI());
if (CommonUtils.isNotBlank(request.getQueryString())) {
final int location = request.getQueryString().indexOf(
artifactParameterName + "=");
final int location = request.getQueryString().indexOf(artifactParameterName + "=");
if (location == 0) {
final String returnValue = encode ? response.encodeURL(buffer
.toString()): buffer.toString();
final String returnValue = encode ? response.encodeURL(buffer.toString()): buffer.toString();
if (LOG.isDebugEnabled()) {
LOG.debug("serviceUrl generated: " + returnValue);
}

View File

@ -6,6 +6,8 @@
package org.jasig.cas.client.util;
import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.util.ArrayList;
import java.util.Collection;
@ -104,4 +106,18 @@ public final class CommonUtilsTests extends TestCase {
assertFalse(CommonUtils.isNotBlank(null));
assertFalse(CommonUtils.isNotBlank(" "));
}
public void testConstructServiceUrlWithTrailingSlash() {
final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/";
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/");
request.setScheme("https");
request.setSecure(true);
final MockHttpServletResponse response = new MockHttpServletResponse();
final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", "ticket", false);
assertEquals(CONST_MY_URL, constructedUrl);
}
}

View File

@ -33,8 +33,7 @@ public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTes
" ResponseID=\"_3b62bece2e8da1c10279db04882012ac\"><Status><StatusCode\n" +
" Value=\"samlp:Responder\"></StatusCode><StatusMessage>Success</StatusMessage></Status></Response></SOAP-ENV:Body></SOAP-ENV:Envelope>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
PublicTestHttpServer.instance().content = RESPONSE.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.validator.validate("test", "test");
fail("ValidationException expected due to 'no' response");

View File

@ -0,0 +1,62 @@
package org.jasig.cas.client.tomcat;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Abstract base class for Container-managed log out. Removes the attributes
* from the session.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.12
*/
public abstract class AbstractLogoutValveBase extends ValveBase {
protected final Log log = LogFactory.getLog(getClass());
public final void invoke(final Request request, final Response response) throws IOException, ServletException {
if (!isLogoutRequest(request)) {
log.debug("Current request URI [ " + request.getRequestURI() + "] is not a logout request.");
getNext().invoke(request, response);
return;
}
final HttpSession httpSession = request.getSession(false);
if (httpSession != null) {
httpSession.removeAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
}
final String redirectUrl = constructRedirectUrl(request);
if (redirectUrl != null) {
response.sendRedirect(redirectUrl);
}
}
/**
* Determines if this is a request to destroy the container-managed single sign on session.
*
* @param request the request. CANNOT be NULL.
* @return true if it is a logout request, false otherwise.
*/
protected abstract boolean isLogoutRequest(Request request);
/**
* Constructs a url to redirect to.
*
* @param request the original request.
* @return the url to redirect to. CAN be NULL.
*/
protected abstract String constructRedirectUrl(Request request);
}

View File

@ -3,9 +3,13 @@ package org.jasig.cas.client.tomcat;
import org.apache.catalina.authenticator.AuthenticatorBase;
import org.apache.catalina.connector.Request;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -21,6 +25,8 @@ public abstract class CasAuthenticator extends AuthenticatorBase {
private static final String INFO = "org.jasig.cas.client.tomcat.CasAuthenticator/1.0";
private static final Log log = LogFactory.getLog(CasAuthenticator.class);
private String serverName;
private String serviceUrl;
@ -35,6 +41,8 @@ public abstract class CasAuthenticator extends AuthenticatorBase {
protected abstract String getServiceParameterName();
private TicketValidator ticketValidator;
public String getInfo() {
return INFO;
}
@ -43,25 +51,38 @@ public abstract class CasAuthenticator extends AuthenticatorBase {
final Assertion assertion = (Assertion) request.getSession(true).getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
if (assertion != null) {
return true;
return isKnownUser(assertion);
}
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
if (CommonUtils.isBlank(ticket)) {
final String serviceUrl = CommonUtils.constructServiceUrl(request, httpServletResponse, this.serviceUrl, this.serverName, getArtifactParameterName(), this.encode);
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, false);
httpServletResponse.sendRedirect(urlToRedirectTo);
return false;
}
final Principal principal = this.context.getRealm().authenticate(null, ticket);
return false; //To change body of implemented methods use File | Settings | File Templates.
}
protected boolean isKnownUser(final Assertion assertion) {
return true;
}
protected boolean isAuthenticatedRequest(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
final String serviceUrl = CommonUtils.constructServiceUrl(request, response, this.serviceUrl, this.serverName, getArtifactParameterName(), this.encode);
if (CommonUtils.isBlank(ticket)) {
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, false);
response.sendRedirect(urlToRedirectTo);
return false;
}
try {
final Assertion assertion = this.ticketValidator.validate(ticket, serviceUrl);
request.getSession(true).setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, assertion);
return isKnownUser(assertion);
} catch (final TicketValidationException e) {
return false;
}
}
}

View File

@ -0,0 +1,66 @@
package org.jasig.cas.client.tomcat;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* Handles watching a url for the proxy callback.
* <p>
* Because its tough to share state between valves, we expose the storage mechanism via a static variable.
* <p>
* This valve should be ordered before the authentication valves.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.12
*/
public final class ProxyCallbackValve extends ValveBase {
private static ProxyGrantingTicketStorage PROXY_GRANTING_TICKET_STORAGE;
private String proxyGrantingTicketStorageClass;
private String proxyCallbackUrl;
public static ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
return PROXY_GRANTING_TICKET_STORAGE;
}
public void setProxyGrantingTicketStorageClass(final String proxyGrantingTicketStorageClass) {
this.proxyGrantingTicketStorageClass = proxyGrantingTicketStorageClass;
}
public void setProxyCallbackUrl(final String proxyCallbackUrl) {
this.proxyCallbackUrl = proxyCallbackUrl;
}
protected void startInternal() throws LifecycleException {
super.startInternal();
try {
CommonUtils.assertNotNull(this.proxyCallbackUrl, "the proxy callback url cannot be null");
CommonUtils.assertTrue(this.proxyCallbackUrl.startsWith("/"), "proxy callback url must start with \"/\"");
final Class proxyGrantingTicketStorage = Class.forName(proxyGrantingTicketStorageClass);
PROXY_GRANTING_TICKET_STORAGE = (ProxyGrantingTicketStorage) proxyGrantingTicketStorage.newInstance();
} catch (final Exception e) {
throw new LifecycleException(e);
}
}
public void invoke(final Request request, final Response response) throws IOException, ServletException {
if (this.proxyCallbackUrl.equals(request.getRequestURI())) {
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, PROXY_GRANTING_TICKET_STORAGE);
return;
}
getNext().invoke(request, response);
}
}

View File

@ -0,0 +1,52 @@
package org.jasig.cas.client.tomcat;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.jasig.cas.client.util.CommonUtils;
import java.util.regex.Pattern;
/**
* Matches a number of urls (based on the regular expression) for handling
* log out.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.12
*/
public final class RegExpBasedLogoutValue extends AbstractLogoutValveBase {
private String regexpUri;
private Pattern regexpUriPattern;
private String redirectUrl;
public void setRegexpUri(final String regexpUri) {
this.regexpUri = regexpUri;
}
public void setRedirectUrl(final String redirectUrl) {
this.redirectUrl = redirectUrl;
}
protected void startInternal() throws LifecycleException {
super.startInternal();
try {
CommonUtils.assertNotNull(this.regexpUri, "A Regular Expression must be provided.");
this.regexpUriPattern = Pattern.compile(this.regexpUri);
} catch (final Exception e) {
throw new LifecycleException(e);
}
}
protected boolean isLogoutRequest(final Request request) {
return this.regexpUriPattern.matcher(request.getRequestURI()).matches();
}
protected String constructRedirectUrl(final Request request) {
return this.redirectUrl;
}
}

View File

@ -0,0 +1,55 @@
package org.jasig.cas.client.tomcat;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.jasig.cas.client.util.CommonUtils;
/**
* Monitors a specific url for logout requests.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.12
*/
public final class UrlBasedLogoutValve extends AbstractLogoutValveBase {
private String logoutUri;
private String redirectUrl;
/**
* The logout url to watch for logout requests.
*
* @param logoutUri the url. CANNOT be null. MUST be relative and start with "/"
*/
public void setLogoutUri(final String logoutUri) {
this.logoutUri = logoutUri;
}
/**
* Optional url to redirect to after logout is complete.
*
* @param redirectUrl the url. CAN be NULL.
*/
public void setRedirectUrl(final String redirectUrl) {
this.redirectUrl = redirectUrl;
}
protected void startInternal() throws LifecycleException {
super.startInternal();
try {
CommonUtils.assertNotNull(this.logoutUri, "logoutUri cannot be null.");
CommonUtils.assertTrue(this.logoutUri.startsWith("/"), "logoutUri must start with \"/\"");
} catch (final IllegalArgumentException e) {
throw new LifecycleException(e);
}
}
protected boolean isLogoutRequest(final Request request) {
return this.logoutUri.equals(request.getRequestURI());
}
protected String constructRedirectUrl(final Request request) {
return redirectUrl;
}
}