RC3
This commit is contained in:
Scott Battaglia 2008-02-01 18:31:59 +00:00
parent 8de11b4830
commit 863df03211
51 changed files with 4027 additions and 0 deletions

64
trunk/assembly.xml Normal file
View File

@ -0,0 +1,64 @@
<assembly>
<id>release</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<useStrictFiltering>false</useStrictFiltering>
<directory>${basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.xml</include>
<include>*.txt</include>
</includes>
</fileSet>
</fileSets>
<moduleSets>
<moduleSet>
<includes></includes>
<sources>
<fileSets>
<fileSet>
<directory>src</directory>
<outputDirectory>src</outputDirectory>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>*.xml</include>
</includes>
</fileSet>
<fileSet>
<lineEnding>unix</lineEnding>
<directory>target/site/apidocs/</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<outputDirectory>docs</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
<includeModuleDirectory>true</includeModuleDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</sources>
<binaries>
<outputDirectory>modules</outputDirectory>
<includeDependencies>true</includeDependencies>
<unpack>false</unpack>
<useDefaultExcludes>true</useDefaultExcludes>
<includes />
</binaries>
</moduleSet>
</moduleSets>
</assembly>

View File

@ -0,0 +1,95 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.jasig.cas</groupId>
<version>3.1-RC3</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-client-core</artifactId>
<packaging>jar</packaging>
<name>JA-SIG CAS Client for Java - Core</name>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>false</filtering>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.4</source>
<target>1.4</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clover-plugin</artifactId>
<configuration>
<licenseLocation>${basedir}/src/test/clover/clover.license</licenseLocation>
</configuration>
<executions>
<execution>
<phase>pre-site</phase>
<goals>
<goal>instrument</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>xml-security</groupId>
<artifactId>xmlsec</artifactId>
<version>1.3.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>1.1b</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>2.5.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
package org.jasig.cas.client.authentication;
import java.io.Serializable;
import java.security.Principal;
import java.util.Map;
/**
* Extension to the standard Java Principal that includes a way to retrieve proxy tickets for a particular user
* and attributes.
* <p>
* Developer's who don't want their code tied to CAS merely need to work with the Java Principal then. Working with
* the CAS-specific features requires knowledge of the AttributePrincipal class.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public interface AttributePrincipal extends Principal, Serializable {
/**
* Retrieves a CAS proxy ticket for this specific principal.
*
* @param service the service we wish to proxy this user to.
* @return a String representing the proxy ticket.
*/
String getProxyTicketFor(String service);
/**
* The Map of key/value pairs associated with this principal.
* @return the map of key/value pairs associated with this principal.
*/
Map getAttributes();
}

View File

@ -0,0 +1,95 @@
package org.jasig.cas.client.authentication;
import org.jasig.cas.client.proxy.ProxyRetriever;
import org.jasig.cas.client.util.CommonUtils;
import java.util.Collections;
import java.util.Map;
/**
* Concrete implementation of the AttributePrincipal interface.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class AttributePrincipalImpl implements AttributePrincipal {
/** Unique Id for Serialization */
private static final long serialVersionUID = -8810123156070148535L;
/** The unique identifier for this principal. */
private final String name;
/** Map of key/value pairs about this principal. */
private final Map attributes;
/** The CAS 2 ticket used to retrieve a proxy ticket. */
private final String proxyGrantingTicket;
/** The method to retrieve a proxy ticket from a CAS server. */
private final ProxyRetriever proxyRetriever;
/**
* Constructs a new principal with an empty map of attributes.
*
* @param name the unique identifier for the principal.
*/
public AttributePrincipalImpl(final String name) {
this(name, Collections.EMPTY_MAP);
}
/**
* Constructs a new principal with the supplied name and attributes.
*
* @param name the unique identifier for the principal.
* @param attributes the key/value pairs for this principal.
*/
public AttributePrincipalImpl(final String name, final Map attributes) {
this(name, attributes, null, null);
}
/**
* Constructs a new principal with the supplied name and the proxying capabilities.
*
* @param name the unique identifier for the principal.
* @param proxyGrantingTicket the ticket associated with this principal.
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server.
*/
public AttributePrincipalImpl(final String name, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
this(name, Collections.EMPTY_MAP, proxyGrantingTicket, proxyRetriever);
}
/**
* Constructs a new principal witht he supplied name, attributes, and proxying capabilities.
*
* @param name the unique identifier for the principal.
* @param attributes the key/value pairs for this principal.
* @param proxyGrantingTicket the ticket associated with this principal.
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server.
*/
public AttributePrincipalImpl(final String name, final Map attributes, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
this.name = name;
this.attributes = attributes;
this.proxyGrantingTicket = proxyGrantingTicket;
this.proxyRetriever = proxyRetriever;
CommonUtils.assertNotNull(this.name, "name cannot be null.");
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
}
public Map getAttributes() {
return this.attributes;
}
public String getProxyTicketFor(String service) {
if (proxyGrantingTicket != null) {
return this.proxyRetriever.getProxyTicketIdFor(this.proxyGrantingTicket, service);
}
return null;
}
public String getName() {
return this.name;
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.authentication;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Filter implementation to intercept all requests and attempt to authenticate
* the user by redirecting them to CAS (unless the user has a ticket).
* <p>
* This filter allows you to specify the following parameters (at either the context-level or the filter-level):
* <ul>
* <li><code>casServerLoginUrl</code> - the url to log into CAS, i.e. https://cas.rutgers.edu/login</li>
* <li><code>renew</code> - true/false on whether to use renew or not.</li>
* <li><code>gateway</code> - true/false on whether to use gateway or not.</li>
* </ul>
*
* <p>Please see AbstractCasFilter for additional properties.</p>
*
* @author Scott Battaglia
* @version $Revision: 11768 $ $Date: 2007-02-07 15:44:16 -0500 (Wed, 07 Feb 2007) $
* @since 3.0
*/
public class AuthenticationFilter extends AbstractCasFilter {
public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";
/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;
/**
* Whether to send the renew request or not.
*/
private boolean renew = false;
/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
setGateway(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
}
public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession(false);
final String ticket = request.getParameter(getArtifactParameterName());
final Assertion assertion = session != null ? (Assertion) session
.getAttribute(CONST_CAS_ASSERTION) : null;
final boolean wasGatewayed = session != null
&& session.getAttribute(CONST_CAS_GATEWAY) != null;
if (CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed) {
log.debug("no ticket and no assertion found");
if (this.gateway) {
log.debug("setting gateway attribute in session");
request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes");
}
final String serviceUrl = constructServiceUrl(request, response);
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, this.gateway);
if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}
response.sendRedirect(urlToRedirectTo);
return;
}
if (session != null) {
log.debug("removing gateway attribute from session");
session.setAttribute(CONST_CAS_GATEWAY, null);
}
filterChain.doFilter(request, response);
}
public final void setRenew(final boolean renew) {
this.renew = renew;
}
public final void setGateway(final boolean gateway) {
this.gateway = gateway;
}
public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.proxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
/**
* Implementation of a ProxyRetriever that follows the CAS 2.0 specification.
* For more information on the CAS 2.0 specification, please see the <a
* href="http://www.ja-sig.org/products/cas/overview/protocol/index.html">specification
* document</a>.
* <p/>
* In general, this class will make a call to the CAS server with some specified
* parameters and receive an XML response to parse.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class Cas20ProxyRetriever implements ProxyRetriever {
/**
* Instance of Commons Logging.
*/
private final Log log = LogFactory.getLog(this.getClass());
/**
* Url to CAS server.
*/
private final String casServerUrl;
/**
* Main Constructor.
*
* @param casServerUrl the URL to the CAS server (i.e. http://localhost/cas/)
*/
public Cas20ProxyRetriever(final String casServerUrl) {
CommonUtils.assertNotNull(casServerUrl,
"casServerUrl cannot be null.");
this.casServerUrl = casServerUrl;
}
public String getProxyTicketIdFor(final String proxyGrantingTicketId,
final String targetService) {
final String url = constructUrl(proxyGrantingTicketId, targetService);
HttpURLConnection conn = null;
try {
final URL constructedUrl = new URL(url);
conn = (HttpURLConnection) constructedUrl.openConnection();
final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
final StringBuffer stringBuffer = new StringBuffer(255);
final String response;
synchronized (stringBuffer) {
while ((line = in.readLine()) != null) {
stringBuffer.append(line);
}
response = stringBuffer.toString();
}
final String error = XmlUtils.getTextForElement(response,
"proxyFailure");
if (CommonUtils.isNotEmpty(error)) {
log.debug(error);
return null;
}
return XmlUtils.getTextForElement(response, "proxyTicket");
} catch (final Exception e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
private String constructUrl(final String proxyGrantingTicketId,
final String targetService) {
try {
return this.casServerUrl + "proxy" + "?pgt="
+ proxyGrantingTicketId + "&targetService="
+ URLEncoder.encode(targetService, "UTF-8");
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.proxy;
/**
* Interface for the storage and retrieval of ProxyGrantingTicketIds by mapping
* them to a specific ProxyGrantingTicketIou.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public interface ProxyGrantingTicketStorage {
/**
* Method to save the ProxyGrantingTicket to the backing storage facility.
*
* @param proxyGrantingTicketIou used as the key
* @param proxyGrantingTicket used as the value
*/
public void save(String proxyGrantingTicketIou, String proxyGrantingTicket);
/**
* Method to retrieve a ProxyGrantingTicket based on the
* ProxyGrantingTicketIou. Note that implementations are not guaranteed to
* return the same result if retrieve is called twice with the same
* proxyGrantingTicketIou.
*
* @param proxyGrantingTicketIou used as the key
* @return the ProxyGrantingTicket Id or null if it can't be found
*/
public String retrieve(String proxyGrantingTicketIou);
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Implementation of {@link ProxyGrantingTicketStorage} that is backed by a
* HashMap that keeps a ProxyGrantingTicket for a specified amount of time.
* <p/>
* A cleanup thread is run periodically to clean out the HashMap.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class ProxyGrantingTicketStorageImpl implements
ProxyGrantingTicketStorage {
/**
* Default timeout in milliseconds.
*/
private static final long DEFAULT_TIMEOUT = 60000;
/**
* Map that stores the PGTIOU to PGT mappings.
*/
private final Map cache = new HashMap();
/**
* Constructor set the timeout to the default value.
*/
public ProxyGrantingTicketStorageImpl() {
this(DEFAULT_TIMEOUT);
}
/**
* Sets the amount of time to hold on to a ProxyGrantingTicket if its never
* been retrieved.
*
* @param timeout the time to hold on to the ProxyGrantingTicket
*/
public ProxyGrantingTicketStorageImpl(final long timeout) {
final Thread thread = new ProxyGrantingTicketCleanupThread(
timeout, this.cache);
thread.setDaemon(true);
thread.start();
}
/**
* NOTE: you can only retrieve a ProxyGrantingTicket once with this method.
* Its removed after retrieval.
*/
public String retrieve(final String proxyGrantingTicketIou) {
final ProxyGrantingTicketHolder holder = (ProxyGrantingTicketHolder) this.cache
.get(proxyGrantingTicketIou);
if (holder == null) {
return null;
}
this.cache.remove(holder);
return holder.getProxyGrantingTicket();
}
public void save(final String proxyGrantingTicketIou,
final String proxyGrantingTicket) {
final ProxyGrantingTicketHolder holder = new ProxyGrantingTicketHolder(
proxyGrantingTicket);
this.cache.put(proxyGrantingTicketIou, holder);
}
private final class ProxyGrantingTicketHolder {
private final String proxyGrantingTicket;
private final long timeInserted;
protected ProxyGrantingTicketHolder(final String proxyGrantingTicket) {
this.proxyGrantingTicket = proxyGrantingTicket;
this.timeInserted = System.currentTimeMillis();
}
public String getProxyGrantingTicket() {
return this.proxyGrantingTicket;
}
final boolean isExpired(final long timeout) {
return System.currentTimeMillis() - this.timeInserted > timeout;
}
}
private final class ProxyGrantingTicketCleanupThread extends Thread {
private final long timeout;
private final Map cache;
public ProxyGrantingTicketCleanupThread(final long timeout,
final Map cache) {
this.timeout = timeout;
this.cache = cache;
}
public void run() {
while (true) {
try {
Thread.sleep(this.timeout);
} catch (final InterruptedException e) {
// nothing to do
}
final List itemsToRemove = new ArrayList();
synchronized (this.cache) {
for (final Iterator iter = this.cache.keySet().iterator(); iter
.hasNext();) {
final Object key = iter.next();
final ProxyGrantingTicketHolder holder = (ProxyGrantingTicketHolder) this.cache
.get(key);
if (holder.isExpired(this.timeout)) {
itemsToRemove.add(key);
}
}
for (final Iterator iter = itemsToRemove.iterator(); iter
.hasNext();) {
this.cache.remove(iter.next());
}
}
}
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.proxy;
/**
* Interface to abstract the retrieval of a proxy ticket to make the
* implementation a black box to the client.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public interface ProxyRetriever {
/**
* Retrieves a proxy ticket for a specific targetService.
*
* @param proxyGrantingTicketId the ProxyGrantingTicketId
* @param targetService the service we want to proxy.
* @return the ProxyTicket Id if Granted, null otherwise.
*/
String getProxyTicketIdFor(String proxyGrantingTicketId,
String targetService);
}

View File

@ -0,0 +1,7 @@
<html>
<body>
<p>The proxy package includes a servlet to act as a proxy receptor,
an interface for ProxyGrantingTicketStorage and an abstraction for
retrieving proxy tickets.</p>
</body>
</html>

View File

@ -0,0 +1,55 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.session;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
/**
* HashMap backed implementation of SessionMappingStorage.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*
*/
public final class HashMapBackedSessionMappingStorage implements
SessionMappingStorage {
/**
* Maps the ID from the CAS server to the Session.
*/
private final Map MANAGED_SESSIONS = new HashMap();
/**
* Maps the Session ID to the key from the CAS Server.
*/
private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap();
public void addSessionById(String mappingId, HttpSession session) {
ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);
MANAGED_SESSIONS.put(mappingId, session);
}
public void removeBySessionById(String sessionId) {
final String key = (String) ID_TO_SESSION_KEY_MAPPING.get(sessionId);
MANAGED_SESSIONS.remove(key);
ID_TO_SESSION_KEY_MAPPING.remove(sessionId);
}
public HttpSession removeSessionByMappingId(String mappingId) {
final HttpSession session = (HttpSession) MANAGED_SESSIONS.get(mappingId);
if (session != null) {
removeBySessionById(session.getId());
}
return session;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.session;
import javax.servlet.http.HttpSession;
/**
* Stores the mapping between sessions and keys to be retrieved later.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*
*/
public interface SessionMappingStorage {
/**
* Remove the HttpSession based on the mappingId.
*
* @param mappingId the id the session is keyed under.
* @return the HttpSession if it exists.
*/
HttpSession removeSessionByMappingId(String mappingId);
/**
* Remove a session by its Id.
* @param sessionId the id of the session.
*/
void removeBySessionById(String sessionId);
/**
* Add a session by its mapping Id.
* @param mappingId the id to map the session to.
* @param session the HttpSession.
*/
void addSessionById(String mappingId, HttpSession session);
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.session;
import org.jasig.cas.client.util.AbstractConfigurationFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Implements the Single Sign Out protocol. It handles registering the session and destroying the session.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class SingleSignOutFilter extends AbstractConfigurationFilter {
/**
* The name of the artifact parameter. This is used to capture the session identifier.
*/
private String artifactParameterName = "ticket";
private static SessionMappingStorage SESSION_MAPPING_STORAGE = new HashMapBackedSessionMappingStorage();
public void init(final FilterConfig filterConfig) throws ServletException {
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
init();
}
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(SESSION_MAPPING_STORAGE, "sessionMappingStorage cannote be null.");
}
public void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
if ("POST".equals(request.getMethod())) {
final String logoutRequest = request.getParameter("logoutRequest");
if (CommonUtils.isNotBlank(logoutRequest)) {
final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
if (CommonUtils.isNotBlank(sessionIdentifier)) {
final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
if (session != null) {
session.invalidate();
}
return;
}
}
} else {
final String artifact = request.getParameter(this.artifactParameterName);
final HttpSession session = request.getSession();
if (CommonUtils.isNotBlank(artifact)) {
SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
SESSION_MAPPING_STORAGE = storage;
}
public static SessionMappingStorage getSessionMappingStorage() {
return SESSION_MAPPING_STORAGE;
}
public void destroy() {
// nothing to do
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.session;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* Listener to detect when an HTTP session is destroyed and remove it from the map of
* managed sessions. Also allows for the programmatic removal of sessions.
* <p>
* Enables the CAS Single Sign out feature.
*
* Scott Battaglia
* @version $Revision$ Date$
* @since 3.1
*/
public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
private SessionMappingStorage SESSION_MAPPING_STORAGE;
public void sessionCreated(final HttpSessionEvent event) {
// nothing to do at the moment
}
public void sessionDestroyed(final HttpSessionEvent event) {
if (SESSION_MAPPING_STORAGE == null) {
SESSION_MAPPING_STORAGE = getSessionMappingStorage();
}
final HttpSession session = event.getSession();
SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
}
/**
* Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same
* instance of the object. It assumes this because it generally lazily calls the method.
*
* @return the SessionMappingStorage
*/
protected static SessionMappingStorage getSessionMappingStorage() {
return SingleSignOutFilter.getSessionMappingStorage();
}
}

View File

@ -0,0 +1,102 @@
package org.jasig.cas.client.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Abstract filter that contains code that is common to all CAS filters.
* <p>
* The following filter options can be configured (either at the context-level or filter-level).
* <ul>
* <li><code>serverName</code> - the name of the CAS server, in the format: localhost:8080 or localhost:8443 or localhost</li>
* <li><code>service</code> - the completely qualified service url, i.e. https://localhost/cas-client/app</li>
* </ul>
* <p>Please note that one of the two above parameters must be set.</p>
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
/** Represents the constant for where the assertion will be located in memory. */
public static final String CONST_CAS_ASSERTION = "_const_cas_assertion_";
/** Instance of commons logging for logging purposes. */
protected final Log log = LogFactory.getLog(getClass());
/** Defines the parameter to look for for the artifact. */
private String artifactParameterName = "ticket";
/** Defines the parameter to look for for the service. */
private String serviceParameterName = "service";
/**
* The name of the server. Should be in the following format: {protocol}:{hostName}:{port}.
* Standard ports can be excluded. */
private String serverName;
/** The exact url of the service. */
private String service;
public final void init(final FilterConfig filterConfig) throws ServletException {
setServerName(getPropertyFromInitParams(filterConfig, "serverName", null));
setService(getPropertyFromInitParams(filterConfig, "service", null));
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
setServiceParameterName(getPropertyFromInitParams(filterConfig, "serviceParameterName", "service"));
initInternal(filterConfig);
init();
}
/** Controls the ordering of filter initialization and checking by defining a method that runs before the init. */
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
// template method
}
/**
* Initialization method. Called by Filter's init method or by Spring.
*/
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.serviceParameterName, "serviceParameterName cannot be null.");
CommonUtils.assertTrue(CommonUtils.isNotEmpty(this.serverName) || CommonUtils.isNotEmpty(this.service), "serverName or service must be set.");
}
public final void destroy() {
// nothing to do
}
protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName);
}
public final void setServerName(final String serverName) {
this.serverName = serverName;
}
public final void setService(final String service) {
this.service = service;
}
public final void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
}
public final void setServiceParameterName(final String serviceParameterName) {
this.serviceParameterName = serviceParameterName;
}
public final String getArtifactParameterName() {
return this.artifactParameterName;
}
public final String getServiceParameterName() {
return this.serviceParameterName;
}
}

View File

@ -0,0 +1,41 @@
package org.jasig.cas.client.util;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Abstracts out the ability to configure the filters from the initial properties provided.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractConfigurationFilter implements Filter {
/**
* Retrieves the property from the FilterConfig. First it checks the FilterConfig's initParameters to see if it
* has a value.
* If it does, it returns that, otherwise it trieves the ServletContext's initParameters and returns that value if any.
*
* @param filterConfig the Filter Configuration.
* @param propertyName the property to retrieve.
* @return the property value, following the above conventions. It will always return the more specific value (i.e.
* filter vs. context).
*/
protected final String getPropertyFromInitParams(final FilterConfig filterConfig, final String propertyName, final String defaultValue) {
final String value = filterConfig.getInitParameter(propertyName);
if (CommonUtils.isNotBlank(value)) {
return value;
}
final String value2 = filterConfig.getServletContext().getInitParameter(propertyName);
if (CommonUtils.isNotBlank(value2)) {
return value2;
}
return defaultValue;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import org.jasig.cas.client.validation.Assertion;
/**
* Static holder that places Assertion in a threadlocal.
*
* @author Scott Battaglia
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public class AssertionHolder {
/**
* ThreadLocal to hold the Assertion for Threads to access.
*/
private static final ThreadLocal threadLocal = new ThreadLocal();
/**
* Retrieve the assertion from the ThreadLocal.
*/
public static Assertion getAssertion() {
return (Assertion) threadLocal.get();
}
/**
* Add the Assertion to the ThreadLocal.
*/
public static void setAssertion(final Assertion assertion) {
threadLocal.set(assertion);
}
/**
* Clear the ThreadLocal.
*/
public static void clear() {
threadLocal.set(null);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import org.jasig.cas.client.validation.Assertion;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Places the assertion in a ThreadLocal such that other resources can access it that do not have access to the web tier session.
*
* @author Scott Battaglia
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class AssertionThreadLocalFilter implements Filter {
public void init(final FilterConfig filterConfig) throws ServletException {
// nothing to do here
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpSession session = request.getSession(false);
final Assertion assertion = (Assertion) (session != null ? session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : null);
try {
AssertionHolder.setAssertion(assertion);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
AssertionHolder.clear();
}
}
public void destroy() {
// nothing to do
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collection;
/**
* Common utilities so that we don't need to include Commons Lang.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class CommonUtils {
/** Instance of Commons Logging. */
private static final Log LOG = LogFactory.getLog(CommonUtils.class);
private CommonUtils() {
// nothing to do
}
/**
* Check whether the object is null or not. If it is, throw an exception and
* display the message.
*
* @param object the object to check.
* @param message the message to display if the object is null.
*/
public static void assertNotNull(final Object object, final String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
/**
* Check whether the collection is null or empty. If it is, throw an
* exception and display the message.
*
* @param c the collecion to check.
* @param message the message to display if the object is null.
*/
public static void assertNotEmpty(final Collection c, final String message) {
assertNotNull(c, message);
if (c.isEmpty()) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the statement is true, otherwise throw an exception with the
* provided message.
*
* @param cond the codition to assert is true.
* @param message the message to display if the condition is not true.
*/
public static void assertTrue(final boolean cond, final String message) {
if (!cond) {
throw new IllegalArgumentException(message);
}
}
/**
* Determines whether the String is null or of length 0.
*
* @param string the string to check
* @return true if its null or length of 0, false otherwise.
*/
public static boolean isEmpty(final String string) {
return string == null || string.length() == 0;
}
/**
* Determines if the String is not empty. A string is not empty if it is not
* null and has a length > 0.
*
* @param string the string to check
* @return true if it is not empty, false otherwise.
*/
public static boolean isNotEmpty(final String string) {
return !isEmpty(string);
}
/**
* Determines if a String is blank or not. A String is blank if its empty or
* if it only contains spaces.
*
* @param string the string to check
* @return true if its blank, false otherwise.
*/
public static boolean isBlank(final String string) {
return isEmpty(string) || string.trim().length() == 0;
}
/**
* Determines if a string is not blank. A string is not blank if it contains
* at least one non-whitespace character.
*
* @param string the string to check.
* @return true if its not blank, false otherwise.
*/
public static boolean isNotBlank(final String string) {
return !isBlank(string);
}
/**
* Constructs the URL to use to redirect to the CAS server.
*
* @param casServerLoginUrl the CAS Server login url.
* @param serviceParameterName the name of the parameter that defines the service.
* @param serviceUrl the actual service's url.
* @param renew whether we should send renew or not.
* @param gateway where we should send gateway or not.
* @return the fully constructed redirect url.
*/
public static final String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName, final String serviceUrl, final boolean renew, final boolean gateway) {
try {
return casServerLoginUrl + "?" + serviceParameterName + "="
+ URLEncoder.encode(serviceUrl, "UTF-8")
+ (renew ? "&renew=true" : "")
+ (gateway ? "&gateway=true" : "");
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Constructs a service url from the HttpServletRequest or from the given
* serviceUrl. Prefers the serviceUrl provided if both a serviceUrl and a
* serviceName.
*
* @param request the HttpServletRequest
* @param response the HttpServletResponse
* @return the service url to use.
*/
public static final String constructServiceUrl(final HttpServletRequest request,
final HttpServletResponse response, final String service, final String serverName, final String artifactParameterName) {
if (CommonUtils.isNotBlank(service)) {
return response.encodeURL(service);
}
final StringBuffer buffer = new StringBuffer();
synchronized (buffer) {
if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
buffer.append(request.isSecure() ? "https://" : "http://");
}
buffer.append(serverName);
buffer.append(request.getRequestURI());
if (CommonUtils.isNotBlank(request.getQueryString())) {
final int location = request.getQueryString().indexOf(
artifactParameterName + "=");
if (location == 0) {
final String returnValue = response.encodeURL(buffer
.toString());
if (LOG.isDebugEnabled()) {
LOG.debug("serviceUrl generated: " + returnValue);
}
return returnValue;
}
buffer.append("?");
if (location == -1) {
buffer.append(request.getQueryString());
} else if (location > 0) {
final int actualLocation = request.getQueryString()
.indexOf("&" + artifactParameterName + "=");
if (actualLocation == -1) {
buffer.append(request.getQueryString());
} else if (actualLocation > 0) {
buffer.append(request.getQueryString().substring(0,
actualLocation));
}
}
}
}
final String returnValue = response.encodeURL(buffer.toString());
if (LOG.isDebugEnabled()) {
LOG.debug("serviceUrl generated: " + returnValue);
}
return returnValue;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import org.jasig.cas.client.validation.Assertion;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.security.Principal;
/**
* Implementation of a filter that wraps the normal HttpServletRequest with a
* wrapper that overrides the getRemoteUser method to retrieve the user from the
* CAS Assertion.
* <p/>
* This filter needs to be configured in the chain so that it executes after
* both the authentication and the validation filters.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class HttpServletRequestWrapperFilter implements Filter {
public void destroy() {
// nothing to do
}
/**
* Wraps the HttpServletRequest in a wrapper class that delegates
* <code>request.getRemoteUser</code> to the underlying Assertion object
* stored in the user session.
*/
public void doFilter(final ServletRequest servletRequest,
final ServletResponse servletResponse, final FilterChain filterChain)
throws IOException, ServletException {
final Principal principal = retrievePrincipalFromSessionOrRequest(servletRequest);
filterChain.doFilter(new CasHttpServletRequestWrapper(
(HttpServletRequest) servletRequest, principal), servletResponse);
}
protected Principal retrievePrincipalFromSessionOrRequest(final ServletRequest servletRequest) {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpSession session = request.getSession(false);
final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));
return assertion == null ? null : assertion.getPrincipal();
}
public void init(final FilterConfig filterConfig) throws ServletException {
// nothing to do
}
final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final Principal principal;
CasHttpServletRequestWrapper(final HttpServletRequest request, final Principal principal) {
super(request);
this.principal = principal;
}
public Principal getUserPrincipal() {
return this.principal;
}
public String getRemoteUser() {
return getUserPrincipal().getName();
}
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/**
* Common utilities for easily parsing XML without duplicating logic.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class XmlUtils {
/**
* Static instance of Commons Logging.
*/
private final static Log LOG = LogFactory.getLog(XmlUtils.class);
/**
* Get an instance of an XML reader from the XMLReaderFactory.
*
* @return the XMLReader.
*/
public static XMLReader getXmlReader() {
try {
return XMLReaderFactory.createXMLReader();
} catch (final SAXException e) {
throw new RuntimeException("Unable to create XMLReader", e);
}
}
/**
* Retrieve the text for a group of elements. Each text element is an entry
* in a list.
* <p>This method is currently optimized for the use case of two elements in a list.
*
* @param xmlAsString the xml response
* @param element the element to look for
* @return the list of text from the elements.
*/
public static List getTextForElements(final String xmlAsString,
final String element) {
final List elements = new ArrayList(2);
final XMLReader reader = getXmlReader();
final DefaultHandler handler = new DefaultHandler() {
private boolean foundElement = false;
private StringBuffer buffer = new StringBuffer();
public void startElement(final String uri, final String localName,
final String qName, final Attributes attributes)
throws SAXException {
if (localName.equals(element)) {
this.foundElement = true;
}
}
public void endElement(final String uri, final String localName,
final String qName) throws SAXException {
if (localName.equals(element)) {
this.foundElement = false;
elements.add(this.buffer.toString());
this.buffer = new StringBuffer();
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
if (this.foundElement) {
this.buffer.append(ch, start, length);
}
}
};
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
try {
reader.parse(new InputSource(new StringReader(xmlAsString)));
} catch (final Exception e) {
LOG.error(e, e);
return null;
}
return elements;
}
/**
* Retrieve the text for a specific element (when we know there is only
* one).
*
* @param xmlAsString the xml response
* @param element the element to look for
* @return the text value of the element.
*/
public static String getTextForElement(final String xmlAsString,
final String element) {
final XMLReader reader = getXmlReader();
final StringBuffer buffer = new StringBuffer();
final DefaultHandler handler = new DefaultHandler() {
private boolean foundElement = false;
public void startElement(final String uri, final String localName,
final String qName, final Attributes attributes)
throws SAXException {
if (localName.equals(element)) {
this.foundElement = true;
}
}
public void endElement(final String uri, final String localName,
final String qName) throws SAXException {
if (localName.equals(element)) {
this.foundElement = false;
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
if (this.foundElement) {
buffer.append(ch, start, length);
}
}
};
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
try {
reader.parse(new InputSource(new StringReader(xmlAsString)));
} catch (final Exception e) {
LOG.error(e, e);
return null;
}
return buffer.toString();
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
<p>The validation package includes interfaces for validating Tickets, as well as the common implementations.</p>
</body>
</html>

View File

@ -0,0 +1,57 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
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.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractCasProtocolUrlBasedTicketValidator extends AbstractUrlBasedTicketValidator {
protected AbstractCasProtocolUrlBasedTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
/**
* 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) {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) validationUrl.openConnection();
final BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
final StringBuffer stringBuffer = new StringBuffer(255);
synchronized (stringBuffer) {
while ((line = in.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append("\n");
}
return stringBuffer.toString();
}
} catch (final IOException e) {
log.error(e,e);
return null;
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}

View File

@ -0,0 +1,182 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* The filter that handles all the work of validating ticket requests.
* <p>
* This filter can be configured with the following values:
* <ul>
* <li><code>redirectAfterValidation</code> - redirect the CAS client to the same URL without the ticket.</li>
* <li><code>exceptionOnValidationFailure</code> - throw an exception if the validation fails. Otherwise, continue
* processing.</li>
* <li><code>useSession</code> - store any of the useful information in a session attribute.</li>
* </ul>
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
/** The TicketValidator we will use to validate tickets. */
private TicketValidator ticketValidator;
/**
* Specify whether the filter should redirect the user agent after a
* successful validation to remove the ticket parameter from the query
* string.
*/
private boolean redirectAfterValidation = false;
/** Determines whether an exception is thrown when there is a ticket validation failure. */
private boolean exceptionOnValidationFailure = true;
private boolean useSession = true;
/**
* Template method to return the appropriate validator.
*
* @param filterConfig the FilterConfiguration that may be needed to construct a validator.
* @return the ticket validator.
*/
protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
return this.ticketValidator;
}
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
super.initInternal(filterConfig);
setExceptionOnValidationFailure(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true")));
setRedirectAfterValidation(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "false")));
setUseSession(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
setTicketValidator(getTicketValidator(filterConfig));
}
public void init() {
super.init();
CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
}
/**
* Pre-process the request before the normal filter process starts. This could be useful for pre-empting code.
*
* @param servletRequest The servlet request.
* @param servletResponse The servlet response.
* @param filterChain the filter chain.
* @return true if processing should continue, false otherwise.
* @throws IOException if there is an I/O problem
* @throws ServletException if there is a servlet problem.
*/
protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
return true;
}
/**
* Template method that gets executed if ticket validation succeeds. Override if you want additional behavior to occur
* if ticket validation succeeds. This method is called after all ValidationFilter processing required for a successful authentication
* occurs.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @param assertion the successful Assertion from the server.
*/
protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, final Assertion assertion) {
// nothing to do here.
}
/**
* Template method that gets executed if validation fails. This method is called right after the exception is caught from the ticket validator
* but before any of the processing of the exception occurs.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
*/
protected void onFailedValidation(final HttpServletRequest request, final HttpServletResponse response) {
// nothing to do here.
}
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
if (!preFilter(servletRequest, servletResponse, filterChain)) {
return;
}
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String ticket = request.getParameter(getArtifactParameterName());
if (CommonUtils.isNotBlank(ticket)) {
if (log.isDebugEnabled()) {
log.debug("Attempting to validate ticket: " + ticket);
}
try {
final Assertion assertion = this.ticketValidator.validate(
ticket, constructServiceUrl(request,
response));
if (log.isDebugEnabled()) {
log.debug("Successfully authenticated user: "
+ assertion.getPrincipal().getName());
}
request.setAttribute(CONST_CAS_ASSERTION, assertion);
if (this.useSession) {
request.getSession().setAttribute(CONST_CAS_ASSERTION,
assertion);
}
onSuccessfulValidation(request, response, assertion);
} catch (final TicketValidationException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
log.warn(e, e);
onFailedValidation(request, response);
if (this.exceptionOnValidationFailure) {
throw new ServletException(e);
}
}
if (this.redirectAfterValidation) {
response.sendRedirect(response
.encodeRedirectURL(constructServiceUrl(request, response)));
return;
}
}
filterChain.doFilter(request, response);
}
public final void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public final void setRedirectAfterValidation(final boolean redirectAfterValidation) {
this.redirectAfterValidation = redirectAfterValidation;
}
public final void setExceptionOnValidationFailure(final boolean exceptionOnValidationFailure) {
this.exceptionOnValidationFailure = exceptionOnValidationFailure;
}
public final void setUseSession(final boolean useSession) {
this.useSession = useSession;
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Abstract validator implementation for tickets that must be validated against a server.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractUrlBasedTicketValidator implements TicketValidator {
/**
* Commons Logging instance.
*/
protected final Log log = LogFactory.getLog(getClass());
/**
* Prefix for the CAS server. Should be everything up to the url endpoint, including the /.
*
* i.e. https://cas.rutgers.edu/
*/
private final String casServerUrlPrefix;
/**
* Whether the request include a renew or not.
*/
private boolean renew;
/**
* Constructs a new TicketValidator with the casServerUrlPrefix.
*
* @param casServerUrlPrefix the location of the CAS server.
*/
protected AbstractUrlBasedTicketValidator(final String casServerUrlPrefix) {
this.casServerUrlPrefix = casServerUrlPrefix;
CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
}
/**
* Template method for ticket validators that need to provide additional parameters to the validation url.
*
* @param urlParameters the map containing the parameters.
*/
protected void populateUrlAttributeMap(final Map urlParameters) {
// nothing to do
}
/**
* The endpoint of the validation URL. Should be relative (i.e. not start with a "/"). I.e. validate or serviceValidate.
* @return the endpoint of the validation URL.
*/
protected abstract String getUrlSuffix();
/**
* Constructs the URL to send the validation request to.
*
* @param ticket the ticket to be validated.
* @param serviceUrl the service identifier.
* @return the fully constructed URL.
*/
protected final String constructValidationUrl(final String ticket, final String serviceUrl) {
final Map urlParameters = new HashMap();
urlParameters.put("ticket", ticket);
urlParameters.put("service", encodeUrl(serviceUrl));
if (this.renew) {
urlParameters.put("renew", "true");
}
populateUrlAttributeMap(urlParameters);
final String suffix = getUrlSuffix();
final StringBuffer buffer = new StringBuffer(urlParameters.size()*10 + this.casServerUrlPrefix.length() + suffix.length() +1);
int i = 0;
synchronized (buffer) {
buffer.append(this.casServerUrlPrefix);
buffer.append("/");
buffer.append(suffix);
for (final Iterator iter = urlParameters.entrySet().iterator(); iter.hasNext();) {
buffer.append(i++ == 0 ? "?" : "&");
final Map.Entry entry = (Map.Entry) iter.next();
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
if (value != null) {
buffer.append(key);
buffer.append("=");
buffer.append(value);
}
}
return buffer.toString();
}
}
/**
* Encodes a URL using the URLEncoder format.
*
* @param url the url to encode.
* @return the encoded url, or the original url if "UTF-8" character encoding could not be found.
*/
protected final String encodeUrl(final String url) {
if (url == null) {
return null;
}
try {
return URLEncoder.encode(url, "UTF-8");
} catch (final UnsupportedEncodingException e) {
return url;
}
}
/**
* Parses the response from the server into a CAS Assertion.
*
* @param response the response from the server, in any format.
* @return the CAS assertion if one could be parsed from the response.
* @throws TicketValidationException if an Assertion could not be created.
*
*/
protected abstract Assertion parseResponseFromServer(final String response) throws TicketValidationException;
/**
* Contacts the CAS Server to retrieve the response for the ticket validation.
*
* @param validationUrl the url to send the validation request to.
* @param ticket the ticket to validate.
* @return the response from the CAS server.
*/
protected abstract String retrieveResponseFromServer(URL validationUrl, String ticket);
public Assertion validate(final String ticket, final String service) throws TicketValidationException {
final String validationUrl = constructValidationUrl(ticket, service);
try {
final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);
if (serverResponse == null) {
throw new TicketValidationException("The CAS server returned no response.");
}
return parseResponseFromServer(serverResponse);
} catch (final MalformedURLException e) {
throw new TicketValidationException(e);
}
}
public void setRenew(final boolean renew) {
this.renew = renew;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.authentication.AttributePrincipal;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
/**
* Represents a response to a validation request.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public interface Assertion extends Serializable {
/**
* The date from which the assertion is valid from.
*
* @return the valid from date.
*/
Date getValidFromDate();
/**
* The date which the assertion is valid until.
*
* @return the valid until date.
*/
Date getValidUntilDate();
/**
* The key/value pairs associated with this assertion.
*
* @return the map of attributes.
*/
Map getAttributes();
/**
* The principal for which this assertion is valid.
*
* @return the principal.
*/
AttributePrincipal getPrincipal();
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.util.CommonUtils;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
/**
* Concrete Implementation of the {@link Assertion}.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*
*/
public final class AssertionImpl implements Assertion {
/** Unique Id for serialization. */
private static final long serialVersionUID = -7767943925833639221L;
/** The date from which the assertion is valid. */
private final Date validFromDate;
/** The date the assertion is valid until. */
private final Date validUntilDate;
/** Map of key/value pairs associated with this assertion. I.e. authentication type. */
private final Map attributes;
/** The principal for which this assertion is valid for. */
private final AttributePrincipal principal;
/**
* Constructs a new Assertion with a Principal of the supplied name, a valid from date of now, no valid until date, and no attributes.
*
* @param name the name of the principal for which this assertion is valid.
*/
public AssertionImpl(final String name) {
this(new AttributePrincipalImpl(name));
}
/**
* Creates a new Assrtion with the supplied Principal.
*
* @param principal the Principal to associate with the Assertion.
*/
public AssertionImpl(final AttributePrincipal principal) {
this(principal, Collections.EMPTY_MAP);
}
/**
* Create a new Assertion with the supplied principal and Assertion attributes.
*
* @param principal the Principal to associate with the Assertion.
* @param attributes the key/value pairs for this attribute.
*/
public AssertionImpl(final AttributePrincipal principal, final Map attributes) {
this(principal, new Date(), null, attributes);
}
/**
* Creats a new Assertion with the supplied principal, Assertion attributes, and start and valid until dates.
*
* @param principal the Principal to associate with the Assertion.
* @param validFromDate when the assertion is valid from.
* @param validUntilDate when the assertion is valid to.
* @param attributes the key/value pairs for this attribute.
*/
public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate, final Map attributes) {
this.principal = principal;
this.validFromDate = validFromDate;
this.validUntilDate = validUntilDate;
this.attributes = attributes;
CommonUtils.assertNotNull(this.principal, "principal cannot be null.");
CommonUtils.assertNotNull(this.validFromDate, "validFromDate cannot be null.");
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
}
public Date getValidFromDate() {
return this.validFromDate;
}
public Date getValidUntilDate() {
return this.validUntilDate;
}
public Map getAttributes() {
return this.attributes;
}
public AttributePrincipal getPrincipal() {
return this.principal;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import javax.servlet.FilterConfig;
/**
* Implementation of AbstractTicketValidatorFilter that instanciates a Cas10TicketValidator.
* <p>Deployers can provide the "casServerPrefix" and the "renew" attributes via the standard context or filter init
* parameters.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter {
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String casUrlServerPrefix = getPropertyFromInitParams(filterConfig, "casUrlServerPrefix", null);
final Cas10TicketValidator validator = new Cas10TicketValidator(casUrlServerPrefix);
validator.setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
return validator;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
/**
* Implementation of a Ticket Validator that can validate tickets conforming to the CAS 1.0 specification.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class Cas10TicketValidator extends AbstractCasProtocolUrlBasedTicketValidator {
public Cas10TicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
protected String getUrlSuffix() {
return "validate";
}
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
if (!response.startsWith("yes")) {
throw new TicketValidationException("CAS Server could not validate ticket.");
}
try {
final BufferedReader reader = new BufferedReader(new StringReader(
response));
reader.readLine();
final String name = reader.readLine();
return new AssertionImpl(name);
} catch (final IOException e) {
throw new TicketValidationException("Unable to parse response.", e);
}
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Creates either a CAS20ProxyTicketValidator or a CAS20ServiceTicketValidator depending on whether any of the
* proxy parameters are set.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*
*/
public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketValidationFilter {
/**
* Constant representing the ProxyGrantingTicket IOU Request Parameter.
*/
private static final String PARAM_PROXY_GRANTING_TICKET_IOU = "pgtIou";
/**
* Constant representing the ProxyGrantingTicket Request Parameter.
*/
private static final String PARAM_PROXY_GRANTING_TICKET = "pgtId";
/**
* The URL to send to the CAS server as the URL that will process proxying requests on the CAS client.
*/
private String proxyReceptorUrl;
/**
* Storage location of ProxyGrantingTickets and Proxy Ticket IOUs.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
super.initInternal(filterConfig);
setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null));
}
public void init() {
super.init();
CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
}
/**
* Constructs a Cas20ServiceTicketValidator or a Cas20ProxyTicketValidator based on supplied parameters.
*
* @param filterConfig the Filter Configuration object.
* @return a fully constructed TicketValidator.
*/
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
final String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null);
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
final Cas20ServiceTicketValidator validator;
if (CommonUtils.isNotBlank(allowAnyProxy) || CommonUtils.isNotBlank(allowedProxyChains)) {
final Cas20ProxyTicketValidator v = new Cas20ProxyTicketValidator(casServerUrlPrefix);
v.setAcceptAnyProxy(Boolean.parseBoolean(allowAnyProxy));
v.setAllowedProxyChains(constructListOfProxies(allowedProxyChains));
validator = v;
} else {
validator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
}
validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix));
validator.setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
return validator;
}
protected final List constructListOfProxies(final String proxies) {
if (CommonUtils.isBlank(proxies)) {
return new ArrayList();
}
final String[] splitProxies = proxies.split("\n");
final List items = Arrays.asList(splitProxies);
final ProxyListPropertyEditor editor = new ProxyListPropertyEditor();
editor.setValue(items);
return (List) editor.getValue();
}
/**
* This processes the ProxyReceptor request before the ticket validation code executes.
*/
protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String requestUri = request.getRequestURI();
if (CommonUtils.isEmpty(this.proxyReceptorUrl) || !requestUri.endsWith(this.proxyReceptorUrl)) {
return true;
}
final String proxyGrantingTicketIou = request
.getParameter(PARAM_PROXY_GRANTING_TICKET_IOU);
final String proxyGrantingTicket = request
.getParameter(PARAM_PROXY_GRANTING_TICKET);
if (CommonUtils.isBlank(proxyGrantingTicket)
|| CommonUtils.isBlank(proxyGrantingTicketIou)) {
response.getWriter().write("");
return false;
}
if (log.isDebugEnabled()) {
log.debug("Received proxyGrantingTicketId ["
+ proxyGrantingTicket + "] for proxyGrantingTicketIou ["
+ proxyGrantingTicketIou + "]");
}
this.proxyGrantingTicketStorage.save(proxyGrantingTicketIou,
proxyGrantingTicket);
response.getWriter().write("<?xml version=\"1.0\"?>");
response.getWriter().write("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\" />");
return false;
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorUrl = proxyReceptorUrl;
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.util.XmlUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Extension to the traditional Service Ticket validation that will validate service tickets and proxy tickets.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator {
private boolean acceptAnyProxy;
/** This should be a list of an array of Strings */
private List allowedProxyChains = new ArrayList();
public Cas20ProxyTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
protected String getUrlSuffix() {
return "proxyValidate";
}
protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException {
final List proxies = XmlUtils.getTextForElements(response, "proxy");
final String[] proxiedList = (String[]) proxies.toArray(new String[proxies.size()]);
// this means there was nothing in the proxy chain, which is okay
if (proxies == null || proxies.isEmpty() || this.acceptAnyProxy) {
return;
}
for (Iterator iter = this.allowedProxyChains.iterator(); iter.hasNext();) {
if (Arrays.equals(proxiedList, (String[]) iter.next())) {
return;
}
}
throw new InvalidProxyChainTicketValidationException("Invalid proxy chain: " + proxies.toString());
}
public void setAcceptAnyProxy(final boolean acceptAnyProxy) {
this.acceptAnyProxy = acceptAnyProxy;
}
public void setAllowedProxyChains(final List allowedProxyChains) {
this.allowedProxyChains = allowedProxyChains;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.SimpleBeanInfo;
import org.springframework.beans.propertyeditors.CustomBooleanEditor;
/**
* BeanInfo support for using this class with Spring. Configures a ProxyListPropertyEditor to be used with the
* Cas20ProxyTicketValidator when Spring is used to configure the CAS client.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class Cas20ProxyTicketValidatorBeanInfo extends SimpleBeanInfo {
public PropertyDescriptor[] getPropertyDescriptors() {
try {
final PropertyDescriptor descriptor = new PropertyDescriptor("allowedProxyChains", Cas20ProxyTicketValidator.class, null, "setAllowedProxyChains") {
public PropertyEditor createPropertyEditor(final Object bean) {
return new ProxyListPropertyEditor();
}
};
final PropertyDescriptor acceptAnyProxy = new PropertyDescriptor("acceptAnyProxy", Cas20ProxyTicketValidator.class, null, "setAcceptAnyProxy") {
public PropertyEditor createPropertyEditor(final Object bean) {
return new CustomBooleanEditor(true);
}
};
return new PropertyDescriptor[] {descriptor, acceptAnyProxy};
} catch (final IntrospectionException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyRetriever;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import java.util.Map;
/**
* Implementation of the TicketValidator that will validate Service Tickets in compliance with the CAS 2.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTicketValidator {
/** The CAS 2.0 protocol proxy callback url. */
private String proxyCallbackUrl;
/** The storage location of the proxy granting tickets. */
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
/** Implementation of the proxy retriever. */
private ProxyRetriever proxyRetriever;
/**
* Constructs an instance of the CAS 2.0 Service Ticket Validator with the supplied
* CAS server url prefix.
*
* @param casServerUrlPrefix the CAS Server URL prefix.
*/
public Cas20ServiceTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix);
}
/**
* Adds the pgtUrl to the list of parameters to pass to the CAS server.
*
* @param urlParameters the Map containing the existing parameters to send to the server.
*/
protected final void populateUrlAttributeMap(final Map urlParameters) {
urlParameters.put("pgtUrl", encodeUrl(this.proxyCallbackUrl));
}
protected String getUrlSuffix() {
return "serviceValidate";
}
protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException {
final String error = XmlUtils.getTextForElement(response,
"authenticationFailure");
if (CommonUtils.isNotBlank(error)) {
throw new TicketValidationException(error);
}
final String principal = XmlUtils.getTextForElement(response, "user");
final String proxyGrantingTicketIou = XmlUtils.getTextForElement(
response, "proxyGrantingTicket");
final String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;
if (CommonUtils.isEmpty(principal)) {
throw new TicketValidationException("No principal was found in the response from the CAS server.");
}
final Assertion assertion;
if (CommonUtils.isNotBlank(proxyGrantingTicket)) {
final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, proxyGrantingTicket, this.proxyRetriever);
assertion = new AssertionImpl(attributePrincipal);
} else {
assertion = new AssertionImpl(principal);
}
customParseResponse(response, assertion);
return assertion;
}
/**
* Template method if additional custom parsing (such as Proxying) needs to be done.
*
* @param response the original response from the CAS server.
* @param assertion the partially constructed assertion.
* @throws TicketValidationException if there is a problem constructing the Assertion.
*/
protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException {
// nothing to do
}
public final void setProxyCallbackUrl(final String proxyCallbackUrl) {
this.proxyCallbackUrl = proxyCallbackUrl;
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
public final void setProxyRetriever(final ProxyRetriever proxyRetriever) {
this.proxyRetriever = proxyRetriever;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
/**
* Exception denotes that an invalid proxy chain was sent from the CAS server to the local application.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class InvalidProxyChainTicketValidationException extends TicketValidationException {
/**
* Unique Id for Serialization
*/
private static final long serialVersionUID = -7736653266370691534L;
/**
* Constructs an exception with the supplied message.
* @param string the supplied message.
*/
public InvalidProxyChainTicketValidationException(final String string) {
super(string);
}
/**
* Constructs an exception with the supplied message and chained throwable.
* @param string the message.
* @param throwable the root exception.
*/
public InvalidProxyChainTicketValidationException(final String string, final Throwable throwable) {
super(string, throwable);
}
/**
* Constructs an exception with the chained throwable.
* @param throwable the root exception.
*/
public InvalidProxyChainTicketValidationException(final Throwable throwable) {
super(throwable);
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import java.beans.PropertyEditorSupport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.jasig.cas.client.util.CommonUtils;
/**
* Convert a String-formatted list of acceptable proxies to an array.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*
*/
public final class ProxyListPropertyEditor extends PropertyEditorSupport {
public void setAsText(final String text) throws IllegalArgumentException {
final BufferedReader reader = new BufferedReader(new StringReader(text));
final List proxyChains = new ArrayList();
try {
String line;
while ((line = reader.readLine()) != null) {
if (CommonUtils.isNotBlank(line)) {
proxyChains.add(line.trim().split(" "));
}
}
} catch (final IOException e) {
// ignore this
} finally {
try {
reader.close();
} catch (final IOException e) {
// nothing to do
}
}
setValue(proxyChains);
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import javax.servlet.FilterConfig;
/**
* Implementation of TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator.
* <p>
* Deployers can provide the "casServerUrlPrefix" and "tolerance" properties of the Saml11TicketValidator via the
* context or filter init parameters.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class Saml11TicketValidationFilter extends AbstractTicketValidationFilter {
public Saml11TicketValidationFilter() {
setArtifactParameterName("SAMLart");
setServiceParameterName("TARGET");
}
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final Saml11TicketValidator validator = new Saml11TicketValidator(getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null));
final String tolerance = getPropertyFromInitParams(filterConfig, "tolerance", "1000");
validator.setTolerance(Long.parseLong(tolerance));
return validator;
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.opensaml.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
/**
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator {
/** Time tolerance to allow for time drifting. */
private long tolerance = 1000L;
public Saml11TicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
protected String getUrlSuffix() {
return "samlValidate";
}
protected void populateUrlAttributeMap(final Map urlParameters) {
final String service = (String) urlParameters.get("service");
urlParameters.remove("service");
urlParameters.remove("ticket");
urlParameters.put("TARGET", service);
}
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final SAMLResponse samlResponse = new SAMLResponse(new ByteArrayInputStream(response.getBytes()));
if (!samlResponse.getAssertions().hasNext()) {
throw new TicketValidationException("No assertions found.");
}
for (final Iterator iter = samlResponse.getAssertions(); iter.hasNext();) {
final SAMLAssertion assertion = (SAMLAssertion) iter.next();
if (!isValidAssertion(assertion)) {
continue;
}
final SAMLAuthenticationStatement authenticationStatement = getSAMLAuthenticationStatement(assertion);
if (authenticationStatement == null) {
throw new TicketValidationException("No AuthentiationStatement found in SAML Assertion.");
}
final SAMLSubject subject = authenticationStatement.getSubject();
if (subject == null) {
throw new TicketValidationException("No Subject found in SAML Assertion.");
}
final SAMLAttribute[] attributes = getAttributesFor(assertion, subject);
final Map personAttributes = new HashMap();
for (int i = 0; i < attributes.length; i++) {
final SAMLAttribute samlAttribute = attributes[i];
final List values = getValuesFrom(samlAttribute);
personAttributes.put(samlAttribute.getName(), values.size() == 1 ? values.get(0) : values);
}
final AttributePrincipal principal = new AttributePrincipalImpl(subject.getNameIdentifier().getName(), personAttributes);
final Map authenticationAttributes = new HashMap();
authenticationAttributes.put("samlAuthenticationStatement::authMethod", authenticationStatement.getAuthMethod());
final Assertion casAssertion = new AssertionImpl(principal, authenticationAttributes);
return casAssertion;
}
} catch (final SAMLException e) {
throw new TicketValidationException(e);
}
throw new TicketValidationException("No valid assertions from the SAML response found.");
}
private boolean isValidAssertion(final SAMLAssertion assertion) {
final Date notBefore = assertion.getNotBefore();
final Date notOnOrAfter = assertion.getNotOnOrAfter();
if (assertion.getNotBefore() == null || assertion.getNotOnOrAfter() == null) {
log.debug("Assertion has no bounding dates. Will not process.");
return false;
}
final long currentTime = new Date().getTime();
if (currentTime + tolerance < notBefore.getTime()) {
log.debug("skipping assertion that's not yet valid...");
return false;
}
if (notOnOrAfter.getTime() <= currentTime - tolerance) {
log.debug("skipping expired assertion...");
return false;
}
return true;
}
private SAMLAuthenticationStatement getSAMLAuthenticationStatement(final SAMLAssertion assertion) {
for (final Iterator iter = assertion.getStatements(); iter.hasNext();) {
final SAMLStatement statement = (SAMLStatement) iter.next();
if (statement instanceof SAMLAuthenticationStatement) {
return (SAMLAuthenticationStatement) statement;
}
}
return null;
}
private SAMLAttribute[] getAttributesFor(final SAMLAssertion assertion, final SAMLSubject subject) {
final List attributes = new ArrayList();
for (final Iterator iter = assertion.getStatements(); iter.hasNext();) {
final SAMLStatement statement = (SAMLStatement) iter.next();
if (statement instanceof SAMLAttributeStatement) {
final SAMLAttributeStatement attributeStatement = (SAMLAttributeStatement) statement;
// used because SAMLSubject does not implement equals
if (subject.getNameIdentifier().getName().equals(attributeStatement.getSubject().getNameIdentifier().getName())) {
for (final Iterator iter2 = attributeStatement.getAttributes(); iter2.hasNext();)
attributes.add(iter2.next());
}
}
}
return (SAMLAttribute[]) attributes.toArray(new SAMLAttribute[attributes.size()]);
}
private List getValuesFrom(final SAMLAttribute attribute) {
final List list = new ArrayList();
for (final Iterator iter = attribute.getValues(); iter.hasNext();) {
list.add(iter.next());
}
return list;
}
protected String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
final String MESSAGE_TO_SEND = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" MajorVersion=\"1\" MinorVersion=\"1\" RequestID=\"_192.168.16.51.1024506224022\" IssueInstant=\"2002-06-19T17:03:44.022Z\">"
+ "<samlp:AssertionArtifact>" + ticket
+ "</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) validationUrl.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Length", Integer.toString(MESSAGE_TO_SEND.length()));
conn.setRequestProperty("SOAPAction", "http://www.oasis-open.org/committees/security");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
final DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.writeBytes(MESSAGE_TO_SEND);
out.flush();
out.close();
final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final StringBuffer buffer = new StringBuffer(256);
synchronized (buffer) {
String line;
while ((line = in.readLine()) != null) {
buffer.append(line);
}
return buffer.toString();
}
} catch (final IOException e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
public void setTolerance(final long tolerance) {
this.tolerance = tolerance;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
/**
* Generic exception to be thrown when ticket validation fails.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class TicketValidationException extends Exception {
/**
* Unique Id for Serialization
*/
private static final long serialVersionUID = -7036248720402711806L;
/**
* Constructs an exception with the supplied message.
*
* @param string the message
*/
public TicketValidationException(final String string) {
super(string);
}
/**
* Constructs an exception with the supplied message and chained throwable.
*
* @param string the message
* @param throwable the original exception
*/
public TicketValidationException(final String string, final Throwable throwable) {
super(string, throwable);
}
/**
* Constructs an exception with the chained throwable.
* @param throwable the original exception.
*/
public TicketValidationException(final Throwable throwable) {
super(throwable);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
/**
* Contract for a validator that will confirm the validity of a supplied ticket.
* <p>
* Validator makes no statement about how to validate the ticket or the format of the ticket (other than that it must be a String).
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public interface TicketValidator {
/**
* Attempts to validate a ticket for the provided service.
*
* @param ticket the ticket to attempt to validate.
* @param service the service this ticket is valid for.
* @return an assertion from the ticket.
* @throws TicketValidationException if the ticket cannot be validated.
*
*/
Assertion validate(String ticket, String service) throws TicketValidationException;
}

View File

@ -0,0 +1,106 @@
package org.jasig.cas.client;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Scott Battaglia
* @version $Revision: 11721 $ $Date: 2007-08-09 15:17:44 -0400 (Wed, 09 Aug 2007) $
* @since 3.0
*/
public final class PublicTestHttpServer extends Thread {
private static PublicTestHttpServer httpServer;
public byte[] content;
private byte[] header;
private int port = 80;
public String encoding;
private PublicTestHttpServer(String data, String encoding, String MIMEType,
int port) throws UnsupportedEncodingException {
this(data.getBytes(encoding), encoding, MIMEType, port);
}
private PublicTestHttpServer(byte[] data, String encoding, String MIMEType,
int port) throws UnsupportedEncodingException {
this.content = data;
this.port = port;
this.encoding = encoding;
String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFile 1.0\r\n"
// + "Content-length: " + this.content.length + "\r\n"
+ "Content-type: " + MIMEType + "\r\n\r\n";
this.header = header.getBytes("ASCII");
}
public static synchronized PublicTestHttpServer instance() {
if (httpServer == null) {
try {
httpServer = new PublicTestHttpServer("test", "ASCII",
"text/plain", 8085);
} catch (Exception e) {
throw new RuntimeException(e);
}
httpServer.start();
Thread.yield();
}
return httpServer;
}
public void run() {
try {
ServerSocket server = new ServerSocket(this.port);
System.out.println("Accepting connections on port "
+ server.getLocalPort());
while (true) {
Socket connection = null;
try {
connection = server.accept();
OutputStream out = new BufferedOutputStream(connection
.getOutputStream());
InputStream in = new BufferedInputStream(connection
.getInputStream());
// read the first line only; that's all we need
StringBuffer request = new StringBuffer(80);
while (true) {
int c = in.read();
if (c == '\r' || c == '\n' || c == -1)
break;
request.append((char) c);
}
if (request.toString().startsWith("STOP")) {
connection.close();
break;
}
if (request.toString().indexOf("HTTP/") != -1) {
out.write(this.header);
}
out.write(this.content);
out.flush();
} // end try
catch (IOException e) {
// nothing to do with this IOException
} finally {
if (connection != null)
connection.close();
}
} // end while
} // end try
catch (IOException e) {
System.err.println("Could not start server. Port Occupied");
}
} // end run
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.authentication;
import junit.framework.TestCase;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.AssertionImpl;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
/**
* Tests for the AuthenticationFilter.
*
* @author Scott Battaglia
* @version $Revision: 11753 $ $Date: 2007-01-03 13:37:26 -0500 (Wed, 03 Jan 2007) $
* @since 3.0
*/
public final class AuthenticationFilterTests extends TestCase {
private static final String CAS_SERVICE_URL = "https://localhost:8443/service";
private static final String CAS_LOGIN_URL = "https://localhost:8443/cas/login";
private AuthenticationFilter filter;
protected void setUp() throws Exception {
// TODO CAS_SERVICE_URL, false, CAS_LOGIN_URL
this.filter = new AuthenticationFilter();
final MockFilterConfig config = new MockFilterConfig();
config.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL);
config.addInitParameter("service", "https://localhost:8443/service");
this.filter.init(config);
}
protected void tearDown() throws Exception {
this.filter.destroy();
}
public void testRedirect() throws Exception {
final MockHttpSession session = new MockHttpSession();
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
// nothing to do
}
};
request.setSession(session);
this.filter.doFilter(request, response, filterChain);
assertEquals(CAS_LOGIN_URL + "?service="
+ URLEncoder.encode(CAS_SERVICE_URL, "UTF-8"), response
.getRedirectedUrl());
}
public void testRedirectWithQueryString() throws Exception {
final MockHttpSession session = new MockHttpSession();
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
request.setQueryString("test=12456");
request.setRequestURI("/test");
request.setSecure(true);
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
// nothing to do
}
};
request.setSession(session);
this.filter = new AuthenticationFilter();
final MockFilterConfig config = new MockFilterConfig();
config.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL);
config.addInitParameter("serverName", "localhost:8443");
this.filter.init(config);
this.filter.doFilter(request, response, filterChain);
assertEquals(CAS_LOGIN_URL
+ "?service="
+ URLEncoder.encode("https://localhost:8443"
+ request.getRequestURI() + "?" + request.getQueryString(),
"UTF-8"), response.getRedirectedUrl());
}
public void testAssertion() throws Exception {
final MockHttpSession session = new MockHttpSession();
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
// nothing to do
}
};
request.setSession(session);
session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION,
new AssertionImpl("test"));
this.filter.doFilter(request, response, filterChain);
assertNull(response.getRedirectedUrl());
}
public void testRenew() throws Exception {
final MockHttpSession session = new MockHttpSession();
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
// nothing to do
}
};
this.filter.setRenew(true);
request.setSession(session);
this.filter.doFilter(request, response, filterChain);
assertNotNull(response.getRedirectedUrl());
assertTrue(response.getRedirectedUrl().indexOf("renew=true") != -1);
}
public void testGateway() throws Exception {
final MockHttpSession session = new MockHttpSession();
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
// nothing to do
}
};
request.setSession(session);
this.filter.setRenew(true);
this.filter.setGateway(true);
this.filter.doFilter(request, response, filterChain);
assertNotNull(session.getAttribute(AuthenticationFilter.CONST_CAS_GATEWAY));
assertNotNull(response.getRedirectedUrl());
final MockHttpServletResponse response2 = new MockHttpServletResponse();
this.filter.doFilter(request, response2, filterChain);
assertNull(session.getAttribute(AuthenticationFilter.CONST_CAS_GATEWAY));
assertNull(response2.getRedirectedUrl());
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Collection;
/**
* Tests for the CommonUtils.
*
* @author Scott Battaglia
* @version $Revision: 11731 $ $Date: 2007-09-27 11:27:21 -0400 (Wed, 27 Sep 2007) $
* @since 3.0
*/
public final class CommonUtilsTests extends TestCase {
public void testAssertNotNull() {
final String CONST_MESSAGE = "test";
CommonUtils.assertNotNull(new Object(), CONST_MESSAGE);
try {
CommonUtils.assertNotNull(null, CONST_MESSAGE);
} catch (IllegalArgumentException e) {
assertEquals(CONST_MESSAGE, e.getMessage());
}
}
public void testAssertNotEmpty() {
final String CONST_MESSAGE = "test";
final Collection c = new ArrayList();
c.add(new Object());
CommonUtils.assertNotEmpty(c, CONST_MESSAGE);
try {
CommonUtils.assertNotEmpty(new ArrayList(), CONST_MESSAGE);
} catch (IllegalArgumentException e) {
assertEquals(CONST_MESSAGE, e.getMessage());
}
try {
CommonUtils.assertNotEmpty(null, CONST_MESSAGE);
} catch (IllegalArgumentException e) {
assertEquals(CONST_MESSAGE, e.getMessage());
}
}
public void testAssertTrue() {
final String CONST_MESSAGE = "test";
CommonUtils.assertTrue(true, CONST_MESSAGE);
try {
CommonUtils.assertTrue(false, CONST_MESSAGE);
} catch (IllegalArgumentException e) {
assertEquals(CONST_MESSAGE, e.getMessage());
}
}
public void testIsEmpty() {
assertFalse(CommonUtils.isEmpty("test"));
assertFalse(CommonUtils.isEmpty(" test"));
assertTrue(CommonUtils.isEmpty(""));
assertTrue(CommonUtils.isEmpty(null));
assertFalse(CommonUtils.isEmpty(" "));
}
public void testIsNotEmpty() {
assertTrue(CommonUtils.isNotEmpty("test"));
assertTrue(CommonUtils.isNotEmpty(" test"));
assertFalse(CommonUtils.isNotEmpty(""));
assertFalse(CommonUtils.isNotEmpty(null));
assertTrue(CommonUtils.isNotEmpty(" "));
}
public void testIsBlank() {
assertFalse(CommonUtils.isBlank("test"));
assertFalse(CommonUtils.isBlank(" test"));
assertTrue(CommonUtils.isBlank(""));
assertTrue(CommonUtils.isBlank(null));
assertTrue(CommonUtils.isBlank(" "));
}
public void testIsNotBlank() {
assertTrue(CommonUtils.isNotBlank("test"));
assertTrue(CommonUtils.isNotBlank(" test"));
assertFalse(CommonUtils.isNotBlank(""));
assertFalse(CommonUtils.isNotBlank(null));
assertFalse(CommonUtils.isNotBlank(" "));
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.util;
import junit.framework.TestCase;
import org.jasig.cas.client.validation.AssertionImpl;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Tests for the HttpServletRequestWrapperFilter.
*
* @author Scott Battaglia
* @version $Revision: 11742 $ $Date: 2007-10-05 14:03:58 -0400 (Thu, 05 Oct 2007) $
* @since 3.0
*/
public final class HttpServletRequestWrapperFilterTests extends TestCase {
private HttpServletRequestWrapperFilter filter = new HttpServletRequestWrapperFilter();
protected HttpServletRequest mockRequest;
protected void setUp() throws Exception {
this.filter.init(null);
this.filter.destroy();
}
public void testWrappedRequest() throws Exception {
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpSession session = new MockHttpSession();
final FilterChain filterChain = new FilterChain() {
public void doFilter(ServletRequest request,
ServletResponse response) throws IOException, ServletException {
HttpServletRequestWrapperFilterTests.this.mockRequest = (HttpServletRequest) request;
}
};
session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION,
new AssertionImpl("test"));
request.setSession(session);
this.filter.doFilter(request, new MockHttpServletResponse(),
filterChain);
assertEquals("test", this.mockRequest.getRemoteUser());
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import junit.framework.TestCase;
/**
* Base class for all TicketValidator tests to inherit from.
*
* @author Scott Battaglia
* @version $Revision: 11731 $ $Date: 2007-09-27 11:27:21 -0400 (Wed, 27 Sep 2007) $
* @since 3.0
*/
public abstract class AbstractTicketValidatorTests extends TestCase {
protected static final String CONST_CAS_SERVER_URL = "http://localhost:8085/";
protected static final String CONST_USERNAME = "username";
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import junit.framework.TestCase;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import java.util.HashMap;
import java.util.Map;
/**
* Test cases for the {@link AssertionImpl}.
*
* @author Scott Battaglia
* @version $Revision: 11737 $ $Date: 2007-10-03 09:14:02 -0400 (Tue, 03 Oct 2007) $
* @since 3.0
*/
public final class AssertionImplTests extends TestCase {
private static final AttributePrincipal CONST_PRINCIPAL = new AttributePrincipalImpl("test");
private static final Map CONST_ATTRIBUTES = new HashMap();
static {
CONST_ATTRIBUTES.put("test", "test");
}
public void testPrincipalConstructor() {
final Assertion assertion = new AssertionImpl(CONST_PRINCIPAL);
assertEquals(CONST_PRINCIPAL, assertion.getPrincipal());
assertTrue(assertion.getAttributes().isEmpty());
assertNull(assertion.getPrincipal().getProxyTicketFor("test"));
}
public void testCompleteConstructor() {
final Assertion assertion = new AssertionImpl(CONST_PRINCIPAL,
CONST_ATTRIBUTES);
assertEquals(CONST_PRINCIPAL, assertion.getPrincipal());
assertEquals(CONST_ATTRIBUTES, assertion.getAttributes());
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.PublicTestHttpServer;
import java.io.UnsupportedEncodingException;
/**
* Test cases for the {@link Cas10TicketValidator}.
*
* @author Scott Battaglia
* @version $Revision: 11731 $ $Date: 2007-09-27 11:27:21 -0400 (Wed, 27 Sep 2007) $
* @since 3.0
*/
public final class Cas10TicketValidatorTests extends AbstractTicketValidatorTests {
private Cas10TicketValidator ticketValidator;
public Cas10TicketValidatorTests() {
super();
}
protected void setUp() throws Exception {
this.ticketValidator = new Cas10TicketValidator(CONST_CAS_SERVER_URL);
}
public void testNoResponse() throws Exception {
PublicTestHttpServer.instance().content = "no\n\n"
.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.ticketValidator.validate("testTicket",
"myService");
fail("ValidationException expected.");
} catch (final TicketValidationException e) {
// expected
}
}
public void testYesResponse() throws TicketValidationException,
UnsupportedEncodingException {
PublicTestHttpServer.instance().content = "yes\nusername\n\n"
.getBytes(PublicTestHttpServer.instance().encoding);
final Assertion assertion = this.ticketValidator.validate("testTicket",
"myService");
assertEquals(CONST_USERNAME, assertion.getPrincipal().getName());
}
public void testBadResponse() throws UnsupportedEncodingException {
PublicTestHttpServer.instance().content = "falalala\n\n"
.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.ticketValidator.validate("testTicket",
"myService");
fail("ValidationException expected.");
} catch (final TicketValidationException e) {
// expected
}
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.PublicTestHttpServer;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.ProxyRetriever;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Test cases for the {@link Cas20ProxyTicketValidator}.
*
* @author Scott Battaglia
* @version $Revision: 11737 $ $Date: 2007-10-03 09:14:02 -0400 (Tue, 03 Oct 2007) $
* @since 3.0
*/
public final class Cas20ProxyTicketValidatorTests extends
AbstractTicketValidatorTests {
private Cas20ProxyTicketValidator ticketValidator;
public Cas20ProxyTicketValidatorTests() {
super();
}
protected void setUp() throws Exception {
final List list = new ArrayList();
list.add(new String[] {"proxy1", "proxy2", "proxy3"});
this.ticketValidator = new Cas20ProxyTicketValidator(CONST_CAS_SERVER_URL);
this.ticketValidator.setRenew(true);
this.ticketValidator.setProxyCallbackUrl("test");
this.ticketValidator.setProxyGrantingTicketStorage(getProxyGrantingTicketStorage());
this.ticketValidator.setProxyRetriever(getProxyRetriever());
this.ticketValidator.setAllowedProxyChains(list);
}
private ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
final ProxyGrantingTicketStorageImpl proxyGrantingTicketStorageImpl = new ProxyGrantingTicketStorageImpl();
return proxyGrantingTicketStorageImpl;
}
private ProxyRetriever getProxyRetriever() {
final ProxyRetriever proxyRetriever = new ProxyRetriever() {
public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) {
return "test";
}
};
return proxyRetriever;
}
public void testProxyChainWithValidProxy() throws TicketValidationException,
UnsupportedEncodingException {
final String USERNAME = "username";
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>username</cas:user><cas:proxyGrantingTicket>PGTIOU-84678-8a9d...</cas:proxyGrantingTicket><cas:proxies><cas:proxy>proxy1</cas:proxy><cas:proxy>proxy2</cas:proxy><cas:proxy>proxy3</cas:proxy></cas:proxies></cas:authenticationSuccess></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
final Assertion assertion = this.ticketValidator.validate("test",
"test");
assertEquals(USERNAME, assertion.getPrincipal().getName());
}
public void testProxyChainWithInvalidProxy() throws TicketValidationException,
UnsupportedEncodingException {
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>username</cas:user><cas:proxyGrantingTicket>PGTIOU-84678-8a9d...</cas:proxyGrantingTicket><cas:proxies><cas:proxy>proxy7</cas:proxy><cas:proxy>proxy2</cas:proxy><cas:proxy>proxy3</cas:proxy></cas:proxies></cas:authenticationSuccess></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.ticketValidator.validate("test", "test");
fail("Invalid proxy chain");
} catch (InvalidProxyChainTicketValidationException e) {
// expected
}
}
public void testConstructionFromSpringBean() throws TicketValidationException,
UnsupportedEncodingException {
final ApplicationContext context = new ClassPathXmlApplicationContext("classpath:cas20ProxyTicketValidator.xml");
final Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator) context.getBean("proxyTicketValidator");
final Cas20ProxyTicketValidator v2 = (Cas20ProxyTicketValidator) context.getBean("proxyTicketValidatorWithAllowAnyProxy");
final String USERNAME = "username";
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>username</cas:user><cas:proxyGrantingTicket>PGTIOU-84678-8a9d...</cas:proxyGrantingTicket><cas:proxies><cas:proxy>proxy1</cas:proxy><cas:proxy>proxy2</cas:proxy><cas:proxy>proxy3</cas:proxy></cas:proxies></cas:authenticationSuccess></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
final Assertion assertion = v.validate("test",
"test");
assertEquals(USERNAME, assertion.getPrincipal().getName());
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.PublicTestHttpServer;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.ProxyRetriever;
import java.io.UnsupportedEncodingException;
/**
* Test cases for the {@link Cas20ServiceTicketValidator}.
*
* @author Scott Battaglia
* @version $Revision: 11737 $ $Date: 2007-10-03 09:14:02 -0400 (Tue, 03 Oct 2007) $
* @since 3.0
*/
public final class Cas20ServiceTicketValidatorTests extends
AbstractTicketValidatorTests {
private Cas20ServiceTicketValidator ticketValidator;
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
public Cas20ServiceTicketValidatorTests() {
super();
}
public Cas20ServiceTicketValidatorTests(Cas20ServiceTicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
protected void setUp() throws Exception {
this.proxyGrantingTicketStorage = getProxyGrantingTicketStorage();
this.ticketValidator = new Cas20ServiceTicketValidator(CONST_CAS_SERVER_URL);
this.ticketValidator.setProxyCallbackUrl("test");
this.ticketValidator.setProxyGrantingTicketStorage(getProxyGrantingTicketStorage());
this.ticketValidator.setProxyRetriever(getProxyRetriever());
this.ticketValidator.setRenew(true);
}
private ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
final ProxyGrantingTicketStorageImpl proxyGrantingTicketStorageImpl = new ProxyGrantingTicketStorageImpl();
return proxyGrantingTicketStorageImpl;
}
private ProxyRetriever getProxyRetriever() {
final ProxyRetriever proxyRetriever = new ProxyRetriever() {
public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) {
return "test";
}
};
return proxyRetriever;
}
public void testNoResponse() throws UnsupportedEncodingException {
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationFailure code=\"INVALID_TICKET\">Ticket ST-1856339-aA5Yuvrxzpv8Tau1cYQ7 not recognized</cas:authenticationFailure></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.ticketValidator.validate("test", "test");
fail("ValidationException expected due to 'no' response");
} catch (final TicketValidationException e) {
// expected
}
}
public void testYesResponseButNoPgt() throws TicketValidationException,
UnsupportedEncodingException {
final String USERNAME = "username";
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>"
+ USERNAME
+ "</cas:user></cas:authenticationSuccess></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
final Assertion assertion = this.ticketValidator.validate("test",
"test");
assertEquals(USERNAME, assertion.getPrincipal().getName());
}
public void testYesResponseWithPgt() throws TicketValidationException,
UnsupportedEncodingException {
final String USERNAME = "username";
final String PGTIOU = "testPgtIou";
final String PGT = "test";
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>"
+ USERNAME
+ "</cas:user><cas:proxyGrantingTicket>"
+ PGTIOU
+ "</cas:proxyGrantingTicket></cas:authenticationSuccess></cas:serviceResponse>";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
this.proxyGrantingTicketStorage.save(PGTIOU, PGT);
final Assertion assertion = this.ticketValidator.validate("test",
"test");
assertEquals(USERNAME, assertion.getPrincipal().getName());
// assertEquals(PGT, assertion.getProxyGrantingTicketId());
}
public void testInvalidResponse() throws Exception {
final String RESPONSE = "<root />";
PublicTestHttpServer.instance().content = RESPONSE
.getBytes(PublicTestHttpServer.instance().encoding);
try {
this.ticketValidator.validate("test", "test");
fail("ValidationException expected due to invalid response.");
} catch (final TicketValidationException e) {
// expected
}
}
}

View File

@ -0,0 +1,23 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="proxyTicketValidator"
class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
<constructor-arg index="0" value="http://localhost:8085/" />
<property name="allowedProxyChains">
<value>
test test2 test3 test4 test5
mytest mytest1 mytest2 mytest3
proxy1 proxy2 proxy3
</value>
</property>
</bean>
<bean id="proxyTicketValidatorWithAllowAnyProxy"
class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
p:acceptAnyProxy="true">
<constructor-arg index="0" value="http://localhost:8085/" />
</bean>
</beans>

31
trunk/license.txt Normal file
View File

@ -0,0 +1,31 @@
License for Use
Copyright (c) 2007, JA-SIG, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the JA-SIG, Inc. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

126
trunk/pom.xml Normal file
View File

@ -0,0 +1,126 @@
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas</groupId>
<version>3.1-RC3</version>
<artifactId>cas-client</artifactId>
<packaging>pom</packaging>
<name>JA-SIG CAS Client for Java</name>
<description>JA-SIG CAS Client for Java is the integration point for applications that want to speak with a CAS
server, either via the CAS 1.0 or CAS 2.0 protocol.
</description>
<url>http://www.ja-sig.org/products/cas/</url>
<issueManagement>
<system>JIRA</system>
<url>http://www.ja-sig.org/issues</url>
</issueManagement>
<inceptionYear>2006</inceptionYear>
<mailingLists>
<mailingList>
<name>CAS Community Discussion List</name>
<subscribe>http://tp.its.yale.edu/mailman/listinfo/cas</subscribe>
<unsubscribe>http://tp.its.yale.edu/mailman/listinfo/cas</unsubscribe>
<post>cas@tp.its.yale.edu</post>
<archive>http://tp.its.yale.edu/pipermail/cas/</archive>
<otherArchives>
<otherArchive>http://news.gmane.org/gmane.comp.java.jasig.cas.user</otherArchive>
</otherArchives>
</mailingList>
<mailingList>
<name>CAS Developers Discussion List</name>
<subscribe>http://tp.its.yale.edu/mailman/listinfo/cas-dev</subscribe>
<unsubscribe>http://tp.its.yale.edu/mailman/listinfo/cas-dev</unsubscribe>
<post>cas-dev@tp.its.yale.edu</post>
<archive>http://tp.its.yale.edu/pipermail/cas-dev/</archive>
<otherArchives>
<otherArchive>http://news.gmane.org/gmane.comp.java.jasig.cas.devel</otherArchive>
</otherArchives>
</mailingList>
</mailingLists>
<developers>
<developer>
<id>battags</id>
<name>Scott Battaglia</name>
<email>scott_battaglia@rutgers.edu</email>
<url>http://www.scottbattaglia.com</url>
<organization>Rutgers, the State University of New Jersey</organization>
<organizationUrl>http://www.rutgers.edu</organizationUrl>
<roles>
<role>Project Admin</role>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
</developers>
<licenses>
<license>
<name>JA-SIG License for Use</name>
<url>http://www.ja-sig.org/products/cas/overview/license/</url>
<distribution>repo</distribution>
</license>
</licenses>
<organization>
<name>JA-SIG</name>
<url>http://www.ja-sig.org</url>
</organization>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-1</version>
<configuration>
<descriptors>
<descriptor>${basedir}/assembly.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>logkit</groupId>
<artifactId>logkit</artifactId>
</exclusion>
<exclusion>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>jasig</id>
<name>JA-SIG Maven Repository</name>
<url>http://developer.ja-sig.org/maven2/</url>
<layout>default</layout>
</repository>
</repositories>
<modules>
<module>cas-client-core</module>
</modules>
</project>