CASC-80
Add support for setting HostnameVerifier for CAS validation filters in web.xml.
This commit is contained in:
parent
96a31f5afd
commit
4ef10cd168
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
Copyright (C) 2008-2009 Virginia Tech.
|
||||
All rights reserved.
|
||||
|
||||
SEE LICENSE FOR MORE INFORMATION
|
||||
|
||||
Author: Middleware
|
||||
Email: middleware@vt.edu
|
||||
Version: $Revision$
|
||||
Updated: $Date$
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Hostname verifier that performs no host name verification for an SSL peer
|
||||
* such that all hosts are allowed.
|
||||
*
|
||||
* @author Middleware
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class AnyHostnameVerifier implements HostnameVerifier {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
Copyright (C) 2008-2009 Virginia Tech.
|
||||
All rights reserved.
|
||||
|
||||
SEE LICENSE FOR MORE INFORMATION
|
||||
|
||||
Author: Middleware
|
||||
Email: middleware@vt.edu
|
||||
Version: $Revision$
|
||||
Updated: $Date$
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Validates an SSL peer's hostname using a regular expression that a candidate
|
||||
* host must match in order to be verified.
|
||||
*
|
||||
* @author Middleware
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class RegexHostnameVerifier implements HostnameVerifier {
|
||||
/** Allowed hostname pattern */
|
||||
private Pattern pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given regular expression.
|
||||
*
|
||||
* @param regex Regular expression describing allowed hosts.
|
||||
*/
|
||||
public RegexHostnameVerifier(final String regex) {
|
||||
this.pattern = Pattern.compile(regex);
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
return pattern.matcher(hostname).matches();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
Copyright (C) 2008-2009 Virginia Tech.
|
||||
All rights reserved.
|
||||
|
||||
SEE LICENSE FOR MORE INFORMATION
|
||||
|
||||
Author: Middleware
|
||||
Email: middleware@vt.edu
|
||||
Version: $Revision$
|
||||
Updated: $Date$
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Verifies a SSL peer host name based on an explicit whitelist of allowed hosts.
|
||||
*
|
||||
* @author Middleware
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class WhitelistHostnameVerifier implements HostnameVerifier {
|
||||
/** Allowed hosts */
|
||||
private String[] allowedHosts;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given array of allowed hosts.
|
||||
*
|
||||
* @param allowed Array of allowed hosts.
|
||||
*/
|
||||
public WhitelistHostnameVerifier(final String[] allowed) {
|
||||
this.allowedHosts = allowed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given list of allowed hosts.
|
||||
*
|
||||
* @param allowedList Comma-separated list of allowed hosts.
|
||||
*/
|
||||
public WhitelistHostnameVerifier(final String allowedList) {
|
||||
this.allowedHosts = allowedList.split(",\\s*");
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
for (int i = 0; i < this.allowedHosts.length; i++) {
|
||||
if (hostname.equalsIgnoreCase(this.allowedHosts[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,14 +9,16 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
|
|
@ -274,10 +276,23 @@ public final class CommonUtils {
|
|||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final URL constructedUrl) {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
conn = (HttpURLConnection) constructedUrl.openConnection();
|
||||
return getResponseFromServer(constructedUrl, HttpsURLConnection.getDefaultHostnameVerifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param constructedUrl the url to contact.
|
||||
* @param hostnameVerifier Host name verifier to use for HTTPS connections.
|
||||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final URL constructedUrl, final HostnameVerifier hostnameVerifier) {
|
||||
URLConnection conn = null;
|
||||
try {
|
||||
conn = constructedUrl.openConnection();
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
|
||||
}
|
||||
final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
String line;
|
||||
|
|
@ -294,13 +309,12 @@ public final class CommonUtils {
|
|||
LOG.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
if (conn != null && conn instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)conn).disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ package org.jasig.cas.client.validation;
|
|||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Abstract class that knows the protocol for validating a CAS ticket.
|
||||
|
|
@ -30,6 +26,10 @@ public abstract class AbstractCasProtocolUrlBasedTicketValidator extends Abstrac
|
|||
* Retrieves the response from the server by opening a connection and merely reading the response.
|
||||
*/
|
||||
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
|
||||
return CommonUtils.getResponseFromServer(validationUrl);
|
||||
if (this.hostnameVerifier != null) {
|
||||
return CommonUtils.getResponseFromServer(validationUrl, this.hostnameVerifier);
|
||||
} else {
|
||||
return CommonUtils.getResponseFromServer(validationUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package org.jasig.cas.client.validation;
|
|||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
|
@ -16,6 +17,7 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* The filter that handles all the work of validating ticket requests.
|
||||
|
|
@ -55,9 +57,39 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
* @param filterConfig the FilterConfiguration that may be needed to construct a validator.
|
||||
* @return the ticket validator.
|
||||
*/
|
||||
protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
|
||||
protected TicketValidator getTicketValidator(final FilterConfig filterConfig) {
|
||||
return this.ticketValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured {@link HostnameVerifier} to use for HTTPS connections
|
||||
* if one is configured for this filter.
|
||||
* @param filterConfig Servlet filter configuration.
|
||||
* @return Instance of specified host name verifier or null if none specified.
|
||||
*/
|
||||
protected HostnameVerifier getHostnameVerifier(final FilterConfig filterConfig) {
|
||||
final String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
|
||||
log.trace("Using hostnameVerifier parameter: " + className);
|
||||
final String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
|
||||
log.trace("Using hostnameVerifierConfig parameter: " + config);
|
||||
HostnameVerifier verifier = null;
|
||||
if (className != null) {
|
||||
try {
|
||||
final Class verifierClass = Class.forName(className);
|
||||
if (config != null) {
|
||||
final Constructor cons = verifierClass.getConstructor(new Class[] {String.class});
|
||||
verifier = (HostnameVerifier) cons.newInstance(new Object[] {config});
|
||||
} else {
|
||||
verifier = (HostnameVerifier) verifierClass.newInstance();
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid HostnameVerifier class " + className);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Error creating instance of " + className, e);
|
||||
}
|
||||
}
|
||||
return verifier;
|
||||
}
|
||||
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true")));
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
||||
/**
|
||||
* Abstract validator implementation for tickets that must be validated against a server.
|
||||
*
|
||||
|
|
@ -30,6 +32,11 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
* Commons Logging instance.
|
||||
*/
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Hostname verifier used when making an SSL request to the CAS server.
|
||||
*/
|
||||
protected HostnameVerifier hostnameVerifier;
|
||||
|
||||
/**
|
||||
* Prefix for the CAS server. Should be everything up to the url endpoint, including the /.
|
||||
|
|
@ -198,4 +205,8 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
public void setCustomParameters(final Map customParameters) {
|
||||
this.customParameters = customParameters;
|
||||
}
|
||||
|
||||
public void setHostnameVerifier(final HostnameVerifier verifier) {
|
||||
this.hostnameVerifier = verifier;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter
|
|||
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
|
||||
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
|
||||
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
}
|
||||
|
||||
validator.setCustomParameters(additionalParameters);
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class Saml11TicketValidationFilter extends AbstractTicketValidationFilter
|
|||
final String tolerance = getPropertyFromInitParams(filterConfig, "tolerance", "1000");
|
||||
validator.setTolerance(Long.parseLong(tolerance));
|
||||
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import java.util.*;
|
|||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
|
||||
*
|
||||
|
|
@ -175,6 +177,9 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
|
|||
|
||||
try {
|
||||
conn = (HttpURLConnection) validationUrl.openConnection();
|
||||
if (this.hostnameVerifier != null && conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(this.hostnameVerifier);
|
||||
}
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "text/xml");
|
||||
conn.setRequestProperty("Content-Length", Integer.toString(MESSAGE_TO_SEND.length()));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
Copyright (C) 2008-2009 Virginia Tech.
|
||||
All rights reserved.
|
||||
|
||||
SEE LICENSE FOR MORE INFORMATION
|
||||
|
||||
Author: Middleware
|
||||
Email: middleware@vt.edu
|
||||
Version: $Revision$
|
||||
Updated: $Date$
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link RegexHostnameVerifier} class.
|
||||
*
|
||||
* @author Middleware
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class RegexHostnameVerifierTests extends TestCase {
|
||||
|
||||
/**
|
||||
* Test method for {@link RegexHostnameVerifier#verify(String, SSLSession)}.
|
||||
*/
|
||||
public void testVerify() {
|
||||
final RegexHostnameVerifier verifier = new RegexHostnameVerifier("\\w+\\.vt\\.edu");
|
||||
Assert.assertTrue(verifier.verify("a.vt.edu", null));
|
||||
Assert.assertTrue(verifier.verify("host.vt.edu", null));
|
||||
Assert.assertFalse(verifier.verify("1-host.vt.edu", null));
|
||||
Assert.assertFalse(verifier.verify("mallory.example.com", null));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
Copyright (C) 2008-2009 Virginia Tech.
|
||||
All rights reserved.
|
||||
|
||||
SEE LICENSE FOR MORE INFORMATION
|
||||
|
||||
Author: Middleware
|
||||
Email: middleware@vt.edu
|
||||
Version: $Revision$
|
||||
Updated: $Date$
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link WhitelistHostnameVerifier} class.
|
||||
*
|
||||
* @author Middleware
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class WhitelistHostnameVerifierTests extends TestCase {
|
||||
/**
|
||||
* Test method for {@link WhitelistHostnameVerifier#verify(String, SSLSession)}.
|
||||
*/
|
||||
public void testVerify() {
|
||||
final WhitelistHostnameVerifier verifier = new WhitelistHostnameVerifier(
|
||||
"red.vt.edu, green.vt.edu,blue.vt.edu");
|
||||
Assert.assertTrue(verifier.verify("red.vt.edu", null));
|
||||
Assert.assertTrue(verifier.verify("green.vt.edu", null));
|
||||
Assert.assertTrue(verifier.verify("blue.vt.edu", null));
|
||||
Assert.assertFalse(verifier.verify("purple.vt.edu", null));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue