diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..7ab140e --- /dev/null +++ b/.classpath @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..f5db6d1 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + java-client + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/assembly.xml b/assembly.xml new file mode 100644 index 0000000..d668de7 --- /dev/null +++ b/assembly.xml @@ -0,0 +1,39 @@ + + dist + false + + tar.gz + zip + + + + + org.jasig.cas:cas-client-core + org.jasig.cas:cas-client-uportal + + + + ${artifactId}/src + + src + README* + LICENSE* + NOTICE* + pom.xml + + + + bin + true + false + + target/*.jar + + + *uportal*.jar + *spring-mock*.jar + + + + + \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..e2663e0 --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +@mvn clean package assembly:assembly -Ddescriptor=assembly.xml \ No newline at end of file diff --git a/cas-client-core/.classpath b/cas-client-core/.classpath new file mode 100644 index 0000000..6bc534a --- /dev/null +++ b/cas-client-core/.classpath @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cas-client-core/.cvsignore b/cas-client-core/.cvsignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/cas-client-core/.cvsignore @@ -0,0 +1 @@ +target diff --git a/cas-client-core/.project b/cas-client-core/.project new file mode 100644 index 0000000..6581b2e --- /dev/null +++ b/cas-client-core/.project @@ -0,0 +1,15 @@ + + cas-client-core + 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. + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.jdt.core.javanature + + \ No newline at end of file diff --git a/cas-client-core/.settings/org.eclipse.jdt.core.prefs b/cas-client-core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..57e31ff --- /dev/null +++ b/cas-client-core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +#Mon Aug 07 13:41:21 EDT 2006 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.source=1.4 +org.eclipse.jdt.core.compiler.compliance=1.4 diff --git a/cas-client-core/LICENSE.txt b/cas-client-core/LICENSE.txt new file mode 100644 index 0000000..30c8573 --- /dev/null +++ b/cas-client-core/LICENSE.txt @@ -0,0 +1,29 @@ +License for Use + +Copyright (c) 2000 The JA-SIG Collaborative. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. 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. + + 3. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the JA-SIG Collaborative + (http://www.ja-sig.org/)." + +This software is provided by the JA-SIG collaborative "as is" and any expressed +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 JA-SIG collaborative or its 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. \ No newline at end of file diff --git a/cas-client-core/README.txt b/cas-client-core/README.txt new file mode 100644 index 0000000..f5cd476 --- /dev/null +++ b/cas-client-core/README.txt @@ -0,0 +1,28 @@ +CENTRAL AUTHENTICATION SERVICE (CAS) +-------------------------------------------------------------------- +http://www.ja-sig.org/products/cas/ + +1. INTRODUCTION + +The Central Authentication Service (CAS) is the standard mechanism by which web +applications should authenticate users. Any custom applications written benefit +from using CAS. + +Note that CAS provides authentication; that is, it determines that your users +are who they say they are. CAS should not be viewed as an access-control system; +in particular, providers of applications that grant access to anyone who +possesses a NetID should understand that loose affiliates of an organization may +be granted NetIDs. + +The JA-SIG CAS Client for Java is a support library for Java applications to communicate +with the CAS server. + +2. RELEASE INFO + +CAS requires J2SE 1.4 and J2EE1.3. + +Release conents: +* "src/main/java" contains the Java source files for the framework +* "src/test/java" contains the Java source files for CAS's test suite + + diff --git a/cas-client-core/cas-client-core.iml b/cas-client-core/cas-client-core.iml new file mode 100644 index 0000000..fc4fe0c --- /dev/null +++ b/cas-client-core/cas-client-core.iml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cas-client-core/pom.xml b/cas-client-core/pom.xml new file mode 100644 index 0000000..47eac6d --- /dev/null +++ b/cas-client-core/pom.xml @@ -0,0 +1,132 @@ + + + org.jasig.cas + 3.0-SNAPSHOT + cas-client + + 4.0.0 + org.jasig.cas + cas-client-core + jar + JA-SIG CAS Client for Java - Core + 3.0-SNAPSHOT + + + src/main/java + src/test/java + + + src/test/resources + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.4 + 1.4 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Tests* + + + + + org.apache.maven.plugins + maven-clover-plugin + + ${basedir}/src/test/clover/clover.license + + + + pre-site + + instrument + + + + + + + + + + commons-httpclient + commons-httpclient + 3.0.1 + compile + + + + commons-httpclient + commons-httpclient-contrib + 3.0 + compile + + + + org.springframework + spring-web + 2.0-rc2 + compile + + + + org.springframework + spring-context + 2.0-rc2 + compile + + + + org.springframework + spring-mock + 2.0-rc2 + compile + + + + false + + + org.apache.maven.plugins + maven-clover-plugin + + ${basedir}/src/test/clover/clover.license + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + 128m + 512m + + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.apache.maven.plugins + maven-surefire-report-plugin + + + org.codehaus.mojo + jdepend-maven-plugin + + + + diff --git a/cas-client-core/settings.xml b/cas-client-core/settings.xml new file mode 100644 index 0000000..e3e8e4a --- /dev/null +++ b/cas-client-core/settings.xml @@ -0,0 +1,8 @@ + + + central + JA-SIG Maven Repository + http://developer.ja-sig.org/maven/ + jasig + + \ No newline at end of file diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authorization/AuthorizationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/AuthorizationException.java new file mode 100644 index 0000000..5adc35e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/AuthorizationException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +/** + * Exception to be thrown if the user is not authorized to use the system. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class AuthorizationException extends RuntimeException { + + /** + * Unique ID for serialization. + */ + private static final long serialVersionUID = 5912038088650643442L; + + public AuthorizationException() { + super(); + } + + public AuthorizationException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public AuthorizationException(String arg0) { + super(arg0); + } + + public AuthorizationException(Throwable arg0) { + super(arg0); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authorization/CasAuthorizedDecider.java b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/CasAuthorizedDecider.java new file mode 100644 index 0000000..1c944fe --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/CasAuthorizedDecider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +import org.jasig.cas.authentication.principal.Principal; + +/** + * Simple interface for determining whether a Principal is authorized to use the + * application or not. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public interface CasAuthorizedDecider { + + /** + * Determines whether someone can use the system or not. + * + * @param principal the person we are checking + * @return true if authorized, false otherwise. + */ + boolean isAuthorizedToUseApplication(final Principal principal); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImpl.java new file mode 100644 index 0000000..fd20073 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImpl.java @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.client.util.CommonUtils; + +import java.util.List; + +/** + * Default implementation of the CasAuthorizedDecider that delegates to a list + * to check if someone is authorized. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class DefaultCasAuthorizedDeciderImpl implements + CasAuthorizedDecider { + + /** The list of users authorized to use the system. */ + private List users; + + public boolean isAuthorizedToUseApplication(final Principal principal) { + return this.users.contains(principal.getId()); + } + + public void init() { + CommonUtils.assertNotEmpty(this.users, "users cannot be empty."); + } + + public void setUsers(final List users) { + this.users = users; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authorization/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/package.html new file mode 100644 index 0000000..b8c3d55 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authorization/package.html @@ -0,0 +1,7 @@ + + +

The authorization package contains the interface for a simple +abstraction for authorizing users to use an application. It is not a +complete role-based or access control authorization system.

+ + diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractProxyReceptorServlet.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractProxyReceptorServlet.java new file mode 100644 index 0000000..ee84926 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractProxyReceptorServlet.java @@ -0,0 +1,84 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jasig.cas.client.util.CommonUtils; + +/** + * Implementation of an HttpServlet that accepts ProxyGrantingTicketIous and + * ProxyGrantingTickets and stores them in an implementation of + * {@link ProxyGrantingTicketStorage}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public abstract class AbstractProxyReceptorServlet extends HttpServlet { + + /** + * The name we expect the instance of ProxyGrantingTicketStorage to be + * instanciated under in the applicationContext. + */ + public static final String CONST_PROXY_GRANTING_TICKET_STORAGE_BEAN_NAME = "proxyGrantingTicketStorage"; + + /** 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"; + + /** Instance of ProxyGrantingTicketStorage to store ProxyGrantingTickets. */ + private ProxyGrantingTicketStorage proxyGrantingTicketStorage; + + protected final Log logger = LogFactory.getLog(this.getClass()); + + /** Unique Id for Serialization. */ + private static final long serialVersionUID = 8766956323018042995L; + + protected final void doGet(final HttpServletRequest request, + final HttpServletResponse response) throws ServletException, + IOException { + 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; + } + + if (logger.isDebugEnabled()) { + logger.debug("Received proxyGrantingTicketId [" + + proxyGrantingTicket + "] for proxyGrantingTicketIou [" + + proxyGrantingTicketIou + "]"); + } + + this.proxyGrantingTicketStorage.save(proxyGrantingTicketIou, + proxyGrantingTicket); + + response.getWriter().write(""); + response + .getWriter() + .write( + ""); + } + + public final void setProxyGrantingTicketStorage( + final ProxyGrantingTicketStorage proxyGrantingTicketStorage) { + this.proxyGrantingTicketStorage = proxyGrantingTicketStorage; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java new file mode 100644 index 0000000..caa4daa --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java @@ -0,0 +1,98 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.util.XmlUtils; + +/** + * Implementation of a ProxyRetriever that follows the CAS 2.0 specification. + * For more information on the CAS 2.0 specification, please see the specification + * document. + *

+ * 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$ $Date$ + * @since 3.0 + */ +public final class Cas20ProxyRetriever implements ProxyRetriever { + + /** Instance of Commons Logging. */ + protected Log log = LogFactory.getLog(this.getClass()); + + /** Url to CAS server. */ + private String casServerUrl; + + /** Instance of HttpClient for connecting to server. */ + private HttpClient httpClient; + + public String getProxyTicketIdFor(final String proxyGrantingTicketId, + final Service targetService) { + + final String url = constructUrl(proxyGrantingTicketId, targetService + .getId()); + + final GetMethod method = new GetMethod(url); + try { + this.httpClient.executeMethod(method); + final String response = method.getResponseBodyAsString(); + + final String error = XmlUtils.getTextForElement(response, + "proxyFailure"); + + if (CommonUtils.isNotEmpty(error)) { + log.debug(error); + return null; + } + + return XmlUtils.getTextForElement(response, "proxyTicket"); + + } catch (IOException e) { + log.error(e, e); + return null; + } finally { + method.releaseConnection(); + } + } + + 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); + } + } + + public void setCasServerUrl(final String casServerUrl) { + this.casServerUrl = casServerUrl; + } + + public void setHttpClient(final HttpClient httpClient) { + this.httpClient = httpClient; + } + + public void init() { + CommonUtils.assertNotNull(this.casServerUrl, + "casServerUrl cannot be null."); + CommonUtils + .assertNotNull(this.httpClient, "httpClient cannot be null."); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java new file mode 100644 index 0000000..dd6785a --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.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$ $Date$ + * @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); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java new file mode 100644 index 0000000..11e4b05 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java @@ -0,0 +1,141 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.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. + *

+ * A cleanup thread is run periodically to clean out the HashMap. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class ProxyGrantingTicketStorageImpl implements + ProxyGrantingTicketStorage { + + /** + * Default timeout in milliseconds. + */ + private static final long DEFAULT_TIMEOUT = 60000; + + private Map cache = new HashMap(); + + private long timeout = DEFAULT_TIMEOUT; + + /** + * 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); + } + + public void init() throws Exception { + final Thread thread = new ProxyGrantingTicketCleanupThread( + this.timeout, this.cache); + thread.setDaemon(true); + thread.start(); + } + + /** + * 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 void setTimeout(final long timeout) { + this.timeout = timeout; + } + + private 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 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()); + } + } + } + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java new file mode 100644 index 0000000..08228e1 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java @@ -0,0 +1,29 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import org.jasig.cas.authentication.principal.Service; + +/** + * Interface to abstract the retrieval of a proxy ticket to make the + * implementation a black box to the client. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @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, + Service targetService); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServlet.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServlet.java new file mode 100644 index 0000000..8baad5e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServlet.java @@ -0,0 +1,65 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * Implementation of an HttpServlet that accepts ProxyGrantingTicketIous and + * ProxyGrantingTickets and stores them in an implementation of + * {@link ProxyGrantingTicketStorage}. + *

+ * Note that ProxyReceptorServlet attempts to load a + * {@link ProxyGrantingTicketStorage} from the ApplicationContext either via the + * name "proxyGrantingTicketStorage" or by type. One of these two must exist + * within the applicationContext or the initialization of the + * ProxyReceptorServlet will fail. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class SpringConfiguredProxyReceptorServlet extends + AbstractProxyReceptorServlet { + + /** Unique Id for serialization */ + private static final long serialVersionUID = -5642050740265266568L; + + public void init(final ServletConfig servletConfig) throws ServletException { + final WebApplicationContext context = WebApplicationContextUtils + .getRequiredWebApplicationContext(servletConfig.getServletContext()); + + if (context.containsBean(CONST_PROXY_GRANTING_TICKET_STORAGE_BEAN_NAME)) { + this + .setProxyGrantingTicketStorage((ProxyGrantingTicketStorage) context + .getBean(CONST_PROXY_GRANTING_TICKET_STORAGE_BEAN_NAME, + ProxyGrantingTicketStorage.class)); + return; + } + + final Map map = context + .getBeansOfType(ProxyGrantingTicketStorage.class); + + if (map.isEmpty()) { + throw new ServletException("No ProxyGrantingTicketStorage found!"); + } + + if (map.size() > 1) { + throw new ServletException( + "Expecting one ProxyGrantingTicketStorage and found multiple instances."); + } + + setProxyGrantingTicketStorage((ProxyGrantingTicketStorage) map.get(map + .keySet().iterator().next())); + } + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html new file mode 100644 index 0000000..8e28951 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html @@ -0,0 +1,7 @@ + + +

The proxy package includes a servlet to act as a proxy receptor, +an interface for ProxyGrantingTicketStorage and an abstraction for +retrieving proxy tickets.

+ + \ No newline at end of file diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java new file mode 100644 index 0000000..0b3dcd2 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java @@ -0,0 +1,106 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import java.util.Collection; + +/** + * Common utilities so that we don't need to include Commons Lang. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CommonUtils { + + 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); + } + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java new file mode 100644 index 0000000..c4768bc --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java @@ -0,0 +1,145 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.*; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A Delegating Filter looks up a parameter in the request object and matches + * (either exact or using Regular Expressions) the value. If there is a match, + * the associated filter is executed. Otherwise, the normal chain is executed. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class DelegatingFilter implements Filter { + + /** Instance of Commons Logging. */ + private Log log = LogFactory.getLog(this.getClass()); + + /** The request parameter to look for in the Request object. */ + private String requestParameterName; + + /** The map of filters to delegate to and the criteria (as key). */ + private Map delegators = new HashMap(); + + /** The default filter to use if there is no match. */ + private Filter defaultFilter; + + /** + * Whether the key in the delegators map is an exact match or a regular + * expression. + */ + private boolean exactMatch = false; + + public void destroy() { + // nothing to do here + } + + public void doFilter(final ServletRequest request, + final ServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + + final String parameter = request + .getParameter(this.requestParameterName); + + if (CommonUtils.isNotEmpty(parameter)) { + for (final Iterator iter = this.delegators.keySet().iterator(); iter + .hasNext();) { + final String key = (String) iter.next(); + + if ((parameter.equals(key) && this.exactMatch) + || (parameter.matches(key) && !this.exactMatch)) { + final Filter filter = (Filter) this.delegators.get(key); + if (log.isDebugEnabled()) { + log.debug("Match found for parameter [" + + this.requestParameterName + "] with value [" + + parameter + "]. Delegating to filter [" + + filter.getClass().getName() + "]"); + } + filter.doFilter(request, response, filterChain); + return; + } + } + } + + log.debug("No match found for parameter [" + this.requestParameterName + + "] with value [" + parameter + "]"); + + if (this.defaultFilter != null) { + this.defaultFilter.doFilter(request, response, filterChain); + } else { + filterChain.doFilter(request, response); + } + } + + public void init(final FilterConfig filterConfig) throws ServletException { + // nothing to do here. + } + + public void init() { + CommonUtils.assertNotNull(this.requestParameterName, + "requestParameterName cannot be null."); + CommonUtils.assertTrue(!this.delegators.isEmpty(), + "delegators cannot be empty."); + + for (final Iterator iter = this.delegators.keySet().iterator(); iter + .hasNext();) { + final Object object = this.delegators.get(iter.next()); + + if (!Filter.class.isAssignableFrom(object.getClass())) { + throw new IllegalArgumentException( + "All value objects in the delegators map must be filters."); + } + } + } + + /** + * Sets the map of delegating filters. + * + * @param delegators the map of delegators to set. + */ + public void setDelegators(final Map delegators) { + this.delegators = delegators; + } + + /** + * Marks whether the value of the parameter needs to match exactly or not. + * + * @param exactMatch the value of whether we need to match exactly or not. + */ + public void setExactMatch(final boolean exactMatch) { + this.exactMatch = exactMatch; + } + + /** + * Sets the name of the request parameter to monitor. + * + * @param requestParameterName the name of the request parameter. + */ + public void setRequestParameterName(final String requestParameterName) { + this.requestParameterName = requestParameterName; + } + + /** + * Sets the default filter to use if there are no matches. This is optional + * as the filter will just continue on the chain if there is no default. + * + * @param defaultFilter the filter to use by default. + */ + protected void setDefaultFilter(final Filter defaultFilter) { + this.defaultFilter = defaultFilter; + } + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/FilterToBeanProxy.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/FilterToBeanProxy.java new file mode 100644 index 0000000..7d1ed1d --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/FilterToBeanProxy.java @@ -0,0 +1,65 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.*; +import java.io.IOException; + +/** + * Utility class to retrieve a Filter from a Spring-managed configuration file. + * Based on the FilterToBeanProxy class in Acegi Security (but simplified) + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class FilterToBeanProxy implements Filter { + + /** The filter we are proxying. */ + private Filter filter; + + public void destroy() { + // nothing to do + } + + public final void doFilter(final ServletRequest request, + final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + this.filter.doFilter(request, response, chain); + } + + public final void init(final FilterConfig filterConfig) + throws ServletException { + doInit(filterConfig); + } + + protected ApplicationContext getContext(FilterConfig filterConfig) { + return WebApplicationContextUtils + .getRequiredWebApplicationContext(filterConfig.getServletContext()); + } + + public final void doInit(final FilterConfig filterConfig) + throws ServletException { + final String targetBean = filterConfig.getInitParameter("targetBean"); + + if (CommonUtils.isBlank(targetBean)) { + throw new ServletException( + "init-parameter missing: targetBean is required."); + } + + final ApplicationContext ctx = this.getContext(filterConfig); + + if (!ctx.containsBean(targetBean)) { + throw new ServletException("targetBean '" + targetBean + + "' not found in context"); + } + + this.filter = (Filter) ctx.getBean(targetBean, Filter.class); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java new file mode 100644 index 0000000..cbfdfb5 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java @@ -0,0 +1,155 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +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; + +/** + * Common utilities for easily parsing XML without duplicating logic. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @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. + * + * @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(); + 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(); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html new file mode 100644 index 0000000..d6d6b49 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html @@ -0,0 +1,5 @@ + + +

The validation package includes interfaces for validating Tickets, as well as the common implementations.

+ + diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java new file mode 100644 index 0000000..4b01983 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java @@ -0,0 +1,117 @@ +package org.jasig.cas.client.validation; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.util.CommonUtils; + +import java.net.URLEncoder; + +/** + * Abstract class for validating tickets that defines a workflow that all ticket + * validation should follow. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public abstract class AbstractUrlBasedTicketValidator implements + TicketValidator { + + /** Instance of Commons Logging. */ + protected Log log = LogFactory.getLog(this.getClass()); + + /** Url to CAS server. */ + private String casServerUrl; + + /** Whether this client is looking for an authentication from renew. */ + private boolean renew; + + /** Instance of HttpClient for connecting to server. */ + private HttpClient httpClient; + + public final Assertion validate(final String ticketId, final Service service) + throws ValidationException { + final String url = constructURL(ticketId, service); + final String response = getResponseFromURL(url); + + return parseResponse(response); + } + + protected abstract String constructURL(final String ticketId, + final Service service); + + protected abstract Assertion parseResponse(final String response) + throws ValidationException; + + private String getResponseFromURL(final String url) + throws ValidationException { + final GetMethod method = new GetMethod(url); + + try { + this.httpClient.executeMethod(method); + return method.getResponseBodyAsString(); + } catch (Exception e) { + log.error(e, e); + throw new ValidationException( + "Unable to retrieve response from CAS Server.", e); + } finally { + method.releaseConnection(); + } + } + + public final void init() { + CommonUtils.assertNotNull(this.casServerUrl, + "the validationUrl cannot be null"); + CommonUtils + .assertNotNull(this.httpClient, "httpClient cannot be null."); + + afterPropertiesSetInternal(); + } + + /** + * Helper method to encode the service url. + * + * @param service the service url to encode. + * @return the encoded service url. + */ + protected final String getEncodedService(final Service service) { + try { + return URLEncoder.encode(service.getId(), "UTF-8"); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + protected final String getCasServerUrl() { + return this.casServerUrl; + } + + protected final boolean isRenew() { + return this.renew; + } + + public final void setCasServerUrl(final String casServerUrl) { + this.casServerUrl = casServerUrl; + } + + public final void setHttpClient(final HttpClient httpClient) { + this.httpClient = httpClient; + } + + public final void setRenew(final boolean renew) { + this.renew = renew; + } + + /** + * Template method for afterProperties() for subclasses to call. + * + * @throws Exception + */ + protected void afterPropertiesSetInternal() { + // template method + } + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java new file mode 100644 index 0000000..3bfd34e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import java.io.Serializable; +import java.util.Map; + +import org.jasig.cas.authentication.principal.Principal; + +/** + * Interface to represent a successful response from the CAS Server. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public interface Assertion extends Serializable { + + /** + * Method to retrieve the principal. + * + * @return the principal. + */ + Principal getPrincipal(); + + /** + * Map of attributes returned by the CAS server. A client must know what + * attributes he is looking for as CAS makes no claims about what attributes + * are returned. + * + * @return the map of attributes. + */ + Map getAttributes(); + + /** + * Method to retrieve the proxyGrantingTicket Id. + * + * @return the ProxyGrantingTicket Id if one exists, otherwise null. + */ + String getProxyGrantingTicketId(); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java new file mode 100644 index 0000000..2e444db --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.client.util.CommonUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Concrete implementation of an Assertion. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class AssertionImpl implements Assertion { + + /** Unique id for serialization. */ + private static final long serialVersionUID = 1L; + + /** + * Map of the attributes returned by the CAS server. This is optional as the + * CAS server spec makes no mention of attributes. + */ + private final Map attributes; + + /** The principal who was authenticated. */ + private final Principal principal; + + /** The Proxy Granting Ticket Id returned by the server. */ + private final String proxyGrantingTicketId; + + public AssertionImpl(final Principal principal) { + this(principal, null, null); + } + + public AssertionImpl(final Principal principal, final Map attributes, + final String proxyGrantingTicketId) { + CommonUtils.assertNotNull(principal, "principal cannot be null"); + + this.principal = principal; + this.attributes = attributes == null ? new HashMap() : attributes; + this.proxyGrantingTicketId = CommonUtils + .isNotEmpty(proxyGrantingTicketId) ? proxyGrantingTicketId : null; + } + + public Map getAttributes() { + return this.attributes; + } + + public Principal getPrincipal() { + return this.principal; + } + + public String getProxyGrantingTicketId() { + return this.proxyGrantingTicketId; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java new file mode 100644 index 0000000..e45c0fa --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; + +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimplePrincipal; + +/** + * Implementation of TicketValidator that follows the CAS 1.0 protocol. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class Cas10TicketValidator extends AbstractUrlBasedTicketValidator { + + protected String constructURL(final String ticketId, final Service service) { + return getCasServerUrl() + "validate?ticket=" + ticketId + + (isRenew() ? "&renew=true" : "") + "&service=" + + getEncodedService(service); + } + + protected final Assertion parseResponse(final String response) + throws ValidationException { + if (response == null || "no\n\n".equals(response) + || !response.startsWith("yes")) { + throw new ValidationException( + "'No' response returned from server for validation request."); + } + + try { + final BufferedReader reader = new BufferedReader(new StringReader( + response)); + reader.readLine(); + + final Principal principal = new SimplePrincipal(reader.readLine()); + return new AssertionImpl(principal); + } catch (final IOException e) { + throw new ValidationException("Unable to parse response.", e); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java new file mode 100644 index 0000000..07f27fe --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java @@ -0,0 +1,115 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.util.XmlUtils; + +/** + * Implementation of the TicketValidator interface that knows how to handle + * proxy tickets. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator { + + /* List of proxy chains that we accept. */ + private List proxyChains; + + /** Boolean whether we accept any proxy request or not. */ + private boolean acceptAnyProxy; + + protected String getValidationUrlName() { + return "proxyValidate"; + } + + protected Assertion getValidAssertionInternal(final String response, + final Assertion assertion) throws ValidationException { + final List proxies = XmlUtils.getTextForElements(response, "proxy"); + final Service[] principals = new Service[proxies.size()]; + + // this means there was nothing in the proxy chain, which is okay + if (principals.length == 0 || this.acceptAnyProxy) { + return assertion; + } + + int i = 0; + for (final Iterator iter = proxies.iterator(); iter.hasNext();) { + principals[i++] = new SimpleService((String) iter.next()); + } + + boolean found = false; + for (Iterator iter = this.proxyChains.iterator(); iter.hasNext();) { + if (Arrays.equals(principals, (Object[]) iter.next())) { + found = true; + break; + } + } + + if (!found) { + throw new InvalidProxyChainValidationException(); + } + + return new AssertionImpl(assertion.getPrincipal(), assertion + .getAttributes(), assertion.getProxyGrantingTicketId()); + } + + /** + * In your XML configuration file, proxy chains should be defined as + * follows: <list> <value> proxy1 proxy2 proxy3</value> + * <value> proxy2 proxy4 proxy5</value> <value> proxy4 + * proxy5 proxy6</value> </list> + * + * @param proxyChains + */ + public final void setProxyChains(final List proxyChains) { + this.proxyChains = proxyChains; + } + + /** + * Set this flag to true if you don't care where the proxied request came + * from. + * + * @param acceptAnyProxy flag on whether we accept any proxy or not. + */ + public void setAcceptAnyProxy(final boolean acceptAnyProxy) { + this.acceptAnyProxy = acceptAnyProxy; + } + + protected void afterPropertiesSetInternal() { + super.afterPropertiesSetInternal(); + + CommonUtils.assertTrue(this.proxyChains != null || this.acceptAnyProxy, + "proxyChains cannot be null or acceptAnyProxy must be true."); + CommonUtils.assertTrue((this.proxyChains != null && !this.proxyChains + .isEmpty()) + || this.acceptAnyProxy, + "proxyChains cannot be empty or acceptAnyProxy must be true."); + + final List tempProxyChains = new ArrayList(); + for (final Iterator iter = this.proxyChains.iterator(); iter.hasNext();) { + final String[] values = ((String) iter.next()).split(" "); + final Service[] principals = new Service[values.length]; + + for (int i = 0; i < principals.length; i++) { + principals[i] = new SimpleService(values[i]); + } + + tempProxyChains.add(principals); + } + + this.proxyChains = tempProxyChains; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java new file mode 100644 index 0000000..a05c9b7 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java @@ -0,0 +1,107 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimplePrincipal; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.util.XmlUtils; + +/** + * Implementation of TicketValidator that follows the CAS 2.0 protocol (without + * proxying). + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class Cas20ServiceTicketValidator extends + AbstractUrlBasedTicketValidator { + + /** Proxy callback url to send to the CAS server. */ + private Service proxyCallbackUrl; + + /** The storage mechanism for the ProxyGrantingTickets. */ + private ProxyGrantingTicketStorage proxyGrantingTicketStorage; + + protected String constructURL(final String ticketId, + final Service service) { + return getCasServerUrl() + + getValidationUrlName() + + "?ticket=" + + ticketId + + (isRenew() ? "&renew=true" : "") + + "&service=" + + getEncodedService(service) + + (this.proxyCallbackUrl != null ? "&pgtUrl=" + + getEncodedService(this.proxyCallbackUrl) : ""); + } + + protected final Assertion parseResponse(String response) + throws ValidationException { + final String error = XmlUtils.getTextForElement(response, + "authenticationFailure"); + + if (CommonUtils.isNotBlank(error)) { + log.debug("Validation of ticket failed: " + error); + throw new ValidationException(error); + } + + final String principal = XmlUtils.getTextForElement(response, "user"); + final String proxyGrantingTicketIou = XmlUtils.getTextForElement( + response, "proxyGrantingTicket"); + + if (CommonUtils.isEmpty(principal)) { + throw new ValidationException("No principal found."); + } + + if (CommonUtils.isNotBlank(proxyGrantingTicketIou)) { + return getValidAssertionInternal(response, new AssertionImpl( + new SimplePrincipal(principal), null, + this.proxyGrantingTicketStorage + .retrieve(proxyGrantingTicketIou))); + } + + return getValidAssertionInternal(response, new AssertionImpl( + new SimplePrincipal(principal))); + } + + protected String getValidationUrlName() { + return "serviceValidate"; + } + + protected Assertion getValidAssertionInternal(final String response, + final Assertion assertion) throws ValidationException { + return assertion; + } + + /** + * Sets the proxy callback url + * + * @param proxyCallbackUrl the proxycallback url specified for this + * application. + */ + public final void setProxyCallbackUrl(final String proxyCallbackUrl) { + this.proxyCallbackUrl = new SimpleService(proxyCallbackUrl); + } + + /** + * Sets the ProxyGrantingTicketStorage + * + * @param proxyGrantingTicketStorage the storage mechanism to use. + */ + public final void setProxyGrantingTicketStorage( + final ProxyGrantingTicketStorage proxyGrantingTicketStorage) { + this.proxyGrantingTicketStorage = proxyGrantingTicketStorage; + } + + protected void afterPropertiesSetInternal() { + CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, + "proxyGrantingTicketStorage cannot be null"); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainValidationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainValidationException.java new file mode 100644 index 0000000..4ce7bd2 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainValidationException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +/** + * Specific instance of a ValidationException that is thrown when the proxy + * chain does not match what is returned. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class InvalidProxyChainValidationException extends ValidationException { + + /** Unique id for serialization. */ + private static final long serialVersionUID = 1L; + + public InvalidProxyChainValidationException() { + super(); + } + + public InvalidProxyChainValidationException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidProxyChainValidationException(String message) { + super(message); + } + + public InvalidProxyChainValidationException(Throwable cause) { + super(cause); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java new file mode 100644 index 0000000..5eadddc --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java @@ -0,0 +1,31 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import org.jasig.cas.authentication.principal.Service; + +/** + * Interface to encapsulate the validation of a ticket. The inteface is + * specification neutral. Any implementation can be provided, including + * something that parses CAS1 or CAS2 responses. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public interface TicketValidator { + + /** + * Method to validate a ticket for a give Service. + * + * @param ticketId the ticket to validate + * @param service the service to validate the ticket for + * @return the Assertion about the ticket (never null) + * @throws ValidationException if there is a problem validating the ticket. + */ + Assertion validate(String ticketId, Service service) + throws ValidationException; +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/ValidationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ValidationException.java new file mode 100644 index 0000000..ae17505 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ValidationException.java @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +/** + * Implementation of Exception to be thrown when there is an error validating + * the Ticket returned from the CAS server. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class ValidationException extends Exception { + + /** Unique Id for serialization. */ + private static final long serialVersionUID = 1L; + + /** + * Default constructor. + */ + public ValidationException() { + super(); + } + + /** + * Constructor that accepts a message and a chained exception. + * @param message the error message. + * @param cause the exception we are chaining with. + */ + public ValidationException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructor that accepts a message. + * @param message the error message. + */ + public ValidationException(final String message) { + super(message); + } + + /** + * Constructor that accepts a chained exception. + * @param cause the exception we are chaining with. + */ + public ValidationException(final Throwable cause) { + super(cause); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/validation/package.html new file mode 100644 index 0000000..e272813 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/package.html @@ -0,0 +1,5 @@ + + +

This package contains common utilities used within the CAS client classes.

+ + diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/AbstractCasFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/AbstractCasFilter.java new file mode 100644 index 0000000..e3eeae4 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/AbstractCasFilter.java @@ -0,0 +1,181 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jasig.cas.client.util.CommonUtils; + +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.HttpServletResponse; +import java.io.IOException; + +/** + * Abstract class that contains common functionality amongst CAS filters. + *

+ * You must specify the serverName or the serviceUrl. If you specify both, the + * serviceUrl is used over the serverName. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public abstract class AbstractCasFilter implements Filter { + + /** Constant string representing the ticket parameter. */ + public static final String PARAM_TICKET = "ticket"; + + /** + * Constant representing where we store the Assertion in the + * session. + */ + public static final String CONST_ASSERTION = "_cas_assertion_"; + + /** Constant representing where we flag a gatewayed request in the session. */ + public static final String CONST_GATEWAY = "_cas_gateway_"; + + /** Constant representing where we flag a principal. */ + public static final String CONST_PRINCIPAL = "_cas_principal_"; + + /** Instance of Commons Logging. */ + protected final Log log = LogFactory.getLog(this.getClass()); + + /** + * The name of the server in the following format: : where + * port is optional if its a standard port. + */ + private String serverName; + + /** The exact service url to match to. */ + private String serviceUrl; + + /** Whether to store the entry in session or not. Defaults to true. */ + private boolean useSession = true; + + public final void destroy() { + // nothing to do + } + + public final void doFilter(final ServletRequest servletRequest, + final ServletResponse servletResponse, final FilterChain filterChain) + throws IOException, ServletException { + doFilterInternal((HttpServletRequest) servletRequest, + (HttpServletResponse) servletResponse, filterChain); + } + + protected abstract void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, FilterChain filterChain) + throws IOException, ServletException; + + public void init(final FilterConfig filterConfig) throws ServletException { + // nothing to do here + } + + /** + * 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. + */ + protected final String constructServiceUrl(final HttpServletRequest request, + final HttpServletResponse response) { + if (CommonUtils.isNotBlank(this.serviceUrl)) { + return response.encodeURL(this.serviceUrl); + } + + final StringBuffer buffer = new StringBuffer(); + + synchronized (buffer) { + buffer.append(request.isSecure() ? "https://" : "http://"); + buffer.append(this.serverName); + buffer.append(request.getRequestURI()); + + if (CommonUtils.isNotBlank(request.getQueryString())) { + final int location = request.getQueryString().indexOf( + PARAM_TICKET + "="); + + 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("&" + PARAM_TICKET + "="); + + 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; + } + + protected final boolean isUseSession() { + return this.useSession; + } + + protected final void setUseSession(final boolean useSession) { + this.useSession = useSession; + } + + /** + * Sets the serverName which should be of the format : + * where port is optional if its a standard port. Either the serverName or + * the serviceUrl must be set. + * + * @param serverName the : combination. + */ + public final void setServerName(final String serverName) { + this.serverName = serverName; + } + + public final void setServiceUrl(final String serviceUrl) { + this.serviceUrl = serviceUrl; + } + + public final void init() { + CommonUtils.assertTrue(CommonUtils.isNotBlank(this.serverName) + || CommonUtils.isNotBlank(this.serviceUrl), + "either serverName or serviceUrl must be set"); + afterPropertiesSetInternal(); + } + + /** + * Template method for additional checks at initialization. + * + * @throws Exception any exceptions thrown upon initialization. + */ + protected void afterPropertiesSetInternal() { + // template method + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthenticationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthenticationFilter.java new file mode 100644 index 0000000..2180008 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthenticationFilter.java @@ -0,0 +1,86 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import java.io.IOException; +import java.net.URLEncoder; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.Assertion; + +/** + * Filter implementation to intercept all requests and attempt to authenticate + * the user by redirecting them to CAS (unless the user has a ticket). + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasAuthenticationFilter extends AbstractCasFilter { + + /** The URL to the CAS Server login. */ + private String casServerLoginUrl; + + /** Whether to send the renew request or not. */ + private boolean renew; + + /** Whether to send the gateway request or not. */ + private boolean gateway; + + protected void doFilterInternal(final HttpServletRequest request, + final HttpServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + final HttpSession session = request.getSession(isUseSession()); + final String ticket = request.getParameter(PARAM_TICKET); + final Assertion assertion = session != null ? (Assertion) session + .getAttribute(CONST_ASSERTION) : null; + final boolean wasGatewayed = session != null + && session.getAttribute(CONST_GATEWAY) != null; + + if (CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed) { + if (this.gateway && session != null) { + session.setAttribute(CONST_GATEWAY, "yes"); + } + + final String serviceUrl = constructServiceUrl(request, response); + final String urlToRedirectTo = this.casServerLoginUrl + "?service=" + + URLEncoder.encode(serviceUrl, "UTF-8") + + (this.renew ? "&renew=true" : "") + + (this.gateway ? "&gateway=true" : ""); + response.sendRedirect(urlToRedirectTo); + return; + } + + if (session != null) { + session.setAttribute(CONST_GATEWAY, null); + } + + filterChain.doFilter(request, response); + } + + public void setCasServerLoginUrl(final String casServerLoginUrl) { + this.casServerLoginUrl = casServerLoginUrl; + } + + public void setGateway(final boolean gateway) { + this.gateway = gateway; + } + + public void setRenew(final boolean renew) { + this.renew = renew; + } + + protected void afterPropertiesSetInternal() { + CommonUtils.assertNotNull(this.casServerLoginUrl, + "the CAS Server Login URL cannot be null."); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthorizationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthorizationFilter.java new file mode 100644 index 0000000..3ed2737 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasAuthorizationFilter.java @@ -0,0 +1,87 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.client.authorization.AuthorizationException; +import org.jasig.cas.client.authorization.CasAuthorizedDecider; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.Assertion; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Simple filter that attempts to determine if someone is authorized to use the + * system. Assumes that you are protecting the application with the + * AuthenticationFilter such that the Assertion is set in the session. + *

+ * If a user is not authorized to use the application, the response code of 403 + * will be sent to the browser. + *

+ * This filter needs to be configured after both the authentication filter and + * the validation filter. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + * @see CasAuthorizedDecider + */ +public final class CasAuthorizationFilter implements Filter { + + /** + * Decider that determines whether a specified principal has access to the + * resource or not. + */ + private CasAuthorizedDecider decider; + + public void destroy() { + // 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 HttpServletResponse response = (HttpServletResponse) servletResponse; + final Assertion assertion = (Assertion) request.getSession() + .getAttribute(AbstractCasFilter.CONST_ASSERTION); + + if (assertion == null) { + throw new ServletException( + "assertion session attribute expected but not found."); + } + + final Principal principal = assertion.getPrincipal(); + + final boolean authorized = this.decider + .isAuthorizedToUseApplication(principal); + + if (!authorized) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + throw new AuthorizationException(principal.getId() + + " is not authorized to use this application."); + } + + filterChain.doFilter(servletRequest, servletResponse); + } + + public void init(final FilterConfig filterConfig) throws ServletException { + // nothing to do here + + } + + public void setDecider(final CasAuthorizedDecider decider) { + this.decider = decider; + } + + public void init() { + CommonUtils.assertNotNull(this.decider, + "the casAuthorizedDecider cannot be null."); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasValidationFilter.java new file mode 100644 index 0000000..799ddb5 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/CasValidationFilter.java @@ -0,0 +1,112 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.TicketValidator; +import org.jasig.cas.client.validation.ValidationException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Implementation of a Filter that checks for a "ticket" and if one is found, + * will attempt to validate the ticket. On a successful validation, it sets the + * Assertion object into the session. On an unsuccessful validation attempt, it + * sets the response code to 403. + *

+ * This filter needs to be configured after the authentication filter (if that + * filter exists in the chain). + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + * @see TicketValidator + */ +public final class CasValidationFilter extends AbstractCasFilter { + + /** Instance of the ticket validator. */ + 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; + + public void doFilterInternal(final HttpServletRequest request, + final HttpServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + final String ticket = request.getParameter(PARAM_TICKET); + + if (CommonUtils.isNotBlank(ticket)) { + if (log.isDebugEnabled()) { + log.debug("Attempting to validate ticket: " + ticket); + } + + try { + final Assertion assertion = this.ticketValidator.validate( + ticket, new SimpleService(constructServiceUrl(request, + response))); + + if (log.isDebugEnabled()) { + log.debug("Successfully authenticated user: " + + assertion.getPrincipal().getId()); + } + + request.setAttribute(CONST_PRINCIPAL, assertion.getPrincipal()); + + if (isUseSession()) { + request.getSession().setAttribute(CONST_ASSERTION, + assertion); + } + } catch (final ValidationException e) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + log.warn(e, e); + throw new ServletException(e); + } + } + + if (this.redirectAfterValidation) { + response.sendRedirect(response + .encodeRedirectURL(constructServiceUrl(request, response))); + return; + } + + filterChain.doFilter(request, response); + } + + /** + * Sets the ticket validator for validating tickets. + * + * @param ticketValidator the ticket validator instance we want to use. + */ + public void setTicketValidator(final TicketValidator ticketValidator) { + this.ticketValidator = ticketValidator; + } + + /** + * Sets the flag that tells the filter to redirect after the successful + * validation. + * + * @param redirectAfterValidation true if we want to redirect, false + * otherwise. + */ + public void setRedirectAfterValidation(boolean redirectAfterValidation) { + this.redirectAfterValidation = redirectAfterValidation; + } + + protected void afterPropertiesSetInternal() { + CommonUtils.assertNotNull(this.ticketValidator, + "ticketValidator cannot be null."); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilter.java new file mode 100644 index 0000000..3ff3408 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilter.java @@ -0,0 +1,298 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import org.jasig.cas.client.validation.Assertion; +import org.springframework.web.util.WebUtils; + +import javax.servlet.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +/** + * 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. + *

+ * 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$ $Date$ + * @since 3.0 + */ +public final class HttpServletRequestWrapperFilter implements Filter { + + public void destroy() { + // nothing to do + } + + /** + * Wraps the HttpServletRequest in a wrapper class that delegates + * request.getRemoteUser 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 { + filterChain.doFilter(new CasHttpServletRequestWrapper( + (HttpServletRequest) servletRequest), servletResponse); + } + + public void init(final FilterConfig filterConfig) throws ServletException { + // nothing to do + } + + protected final class CasHttpServletRequestWrapper implements + HttpServletRequest { + + private final HttpServletRequest request; + + protected CasHttpServletRequestWrapper(final HttpServletRequest request) { + this.request = request; + } + + public String getAuthType() { + return this.request.getAuthType(); + } + + public Cookie[] getCookies() { + return this.request.getCookies(); + } + + public long getDateHeader(String s) { + return this.request.getDateHeader(s); + } + + public String getHeader(String s) { + return this.request.getHeader(s); + } + + public Enumeration getHeaders(String s) { + return this.request.getHeaders(s); + } + + public Enumeration getHeaderNames() { + return this.request.getHeaderNames(); + } + + public int getIntHeader(String s) { + return this.request.getIntHeader(s); + } + + public String getMethod() { + return this.request.getMethod(); + } + + public String getPathInfo() { + return this.request.getPathInfo(); + } + + public String getPathTranslated() { + return this.request.getPathTranslated(); + } + + public String getContextPath() { + return this.request.getContextPath(); + } + + public String getQueryString() { + return this.request.getQueryString(); + } + + public String getRemoteUser() { + final org.jasig.cas.authentication.principal.Principal p = (org.jasig.cas.authentication.principal.Principal) this.request + .getAttribute(AbstractCasFilter.CONST_PRINCIPAL); + + if (p != null) { + return p.getId(); + } + + final Assertion assertion = (Assertion) WebUtils + .getSessionAttribute(this.request, + AbstractCasFilter.CONST_ASSERTION); + + if (assertion != null) { + return assertion.getPrincipal().getId(); + } + + return null; + } + + public boolean isUserInRole(String s) { + return this.request.isUserInRole(s); + } + + public Principal getUserPrincipal() { + return this.request.getUserPrincipal(); + } + + public String getRequestedSessionId() { + return this.request.getRequestedSessionId(); + } + + public String getRequestURI() { + return this.request.getRequestURI(); + } + + public StringBuffer getRequestURL() { + return this.request.getRequestURL(); + } + + public String getServletPath() { + return this.request.getServletPath(); + } + + public HttpSession getSession(boolean b) { + return this.request.getSession(b); + } + + public HttpSession getSession() { + return this.request.getSession(); + } + + public boolean isRequestedSessionIdValid() { + return this.request.isRequestedSessionIdValid(); + } + + public boolean isRequestedSessionIdFromCookie() { + return this.request.isRequestedSessionIdFromCookie(); + } + + public boolean isRequestedSessionIdFromURL() { + return this.request.isRequestedSessionIdFromURL(); + } + + public boolean isRequestedSessionIdFromUrl() { + return this.request.isRequestedSessionIdFromUrl(); + } + + public Object getAttribute(String s) { + return this.request.getAttribute(s); + } + + public Enumeration getAttributeNames() { + return this.request.getAttributeNames(); + } + + public String getCharacterEncoding() { + return this.request.getCharacterEncoding(); + } + + public void setCharacterEncoding(String s) + throws UnsupportedEncodingException { + this.request.setCharacterEncoding(s); + } + + public int getContentLength() { + return this.request.getContentLength(); + } + + public String getContentType() { + return this.request.getContentType(); + } + + public ServletInputStream getInputStream() throws IOException { + return this.request.getInputStream(); + } + + public String getParameter(String s) { + return this.request.getParameter(s); + } + + public Enumeration getParameterNames() { + return this.request.getParameterNames(); + } + + public String[] getParameterValues(String s) { + return this.request.getParameterValues(s); + } + + public Map getParameterMap() { + return this.request.getParameterMap(); + } + + public String getProtocol() { + return this.request.getProtocol(); + } + + public String getScheme() { + return this.request.getScheme(); + } + + public String getServerName() { + return this.request.getServerName(); + } + + public int getServerPort() { + return this.request.getServerPort(); + } + + public BufferedReader getReader() throws IOException { + return this.request.getReader(); + } + + public String getRemoteAddr() { + return this.request.getRemoteAddr(); + } + + public String getRemoteHost() { + return this.request.getRemoteHost(); + } + + public void setAttribute(String s, Object o) { + this.request.setAttribute(s, o); + } + + public void removeAttribute(String s) { + this.request.removeAttribute(s); + } + + public Locale getLocale() { + return this.request.getLocale(); + } + + public Enumeration getLocales() { + return this.request.getLocales(); + } + + public boolean isSecure() { + return this.request.isSecure(); + } + + public RequestDispatcher getRequestDispatcher(String s) { + return this.request.getRequestDispatcher(s); + } + + public String getRealPath(String s) { + return this.request.getRealPath(s); + } + + public int getRemotePort() { + return this.request.getRemotePort(); + } + + public String getLocalName() { + return this.request.getLocalName(); + } + + public String getLocalAddr() { + return this.request.getLocalAddr(); + } + + public int getLocalPort() { + return this.request.getLocalPort(); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/package.html new file mode 100644 index 0000000..7329dac --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/web/filter/package.html @@ -0,0 +1,5 @@ + + +

This package contains all of the useful filters related to normal CAS processing, including Authentication, Validation and Authorization.

+ + diff --git a/cas-client-core/src/main/resources/cas-client.properties b/cas-client-core/src/main/resources/cas-client.properties new file mode 100644 index 0000000..d2ce241 --- /dev/null +++ b/cas-client-core/src/main/resources/cas-client.properties @@ -0,0 +1,4 @@ +cas.server.gateway=false +cas.server.renew=false +cas.server.url=https://localhost:8443/cas/ +cas.server.proxyCallbackUrl=https://localhost:8443/manager/proxy/Receptor diff --git a/cas-client-core/src/main/resources/simpleFilterExample.xml b/cas-client-core/src/main/resources/simpleFilterExample.xml new file mode 100644 index 0000000..39c7690 --- /dev/null +++ b/cas-client-core/src/main/resources/simpleFilterExample.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + battags + + + + \ No newline at end of file diff --git a/cas-client-core/src/main/resources/web-simple-example.xml b/cas-client-core/src/main/resources/web-simple-example.xml new file mode 100644 index 0000000..f83e745 --- /dev/null +++ b/cas-client-core/src/main/resources/web-simple-example.xml @@ -0,0 +1,82 @@ + + + + + Java CAS Client + + + + contextConfigLocation + + /WEB-INF/simpleFilterExample.xml + + + + + CAS Authentication Filter Proxy + org.jasig.cas.client.util.FilterToBeanProxy + + targetBean + casAuthenticationFilter + + + + + CAS Validation Filter Proxy + org.jasig.cas.client.util.FilterToBeanProxy + + targetBean + casValidationFilter + + + + + CAS Authorization Filter Proxy + org.jasig.cas.client.util.FilterToBeanProxy + + targetBean + casAuthorizationFilter + + + + + CAS Authentication Filter Proxy + /* + + + + CAS Validation Filter Proxy + /* + + + + CAS Authorization Filter Proxy + /* + + + + + org.springframework.web.context.ContextLoaderListener + + + + + casclient + org.jasig.cas.client.proxy.ProxyReceptorServlet + 1 + + + + casclient + /proxy/Receptor + + \ No newline at end of file diff --git a/cas-client-core/src/test/clover/clover.license b/cas-client-core/src/test/clover/clover.license new file mode 100644 index 0000000..8506a9d --- /dev/null +++ b/cas-client-core/src/test/clover/clover.license @@ -0,0 +1,166 @@ +Product: Clover +License: Open Source License, 0.x, 1.x +Issued: Sun Feb 13 2005 22:45:43 CST +Expiry: Never +Key: aae4c7035b1208e316fa6d684 +Name: Scott Battaglia +Org: JA-SIG Central Authentication Service +Certificate: AAACCG+Ow8B7/zEbxOMqqKwwrdpP+a1COmJGHco7sCNLjHkHnajPF+dQW +Ct12PMy0uml0s9xuus5wKngJ9OFk5PFeh01dzQF66bhXH1bvegLfvja3Kle6BYtDv4LZgE +gk3E0aJN4IbgTn+TgUckSevXDR4KzK77NWJfrVzkxV3/JepYRA9UAbsXHiki9WjMIJfzoZ +unjvtTFd/vTOcyirgfT/dTPrG9PAGAjH+e37E9Xf7HnRSrmxtrGX+wdaBOlZFUIIcVHKT2 +IaGToLZnx7FvfE3rzyQCYtS+r0E+H61c+dANzySy5PEH2JEyL8M9JrwgYJSju1FWhxbXO2 +Gb7y3Diufo80+HWz6xrGzl9IlXRseoXHki8rllk5taXqVv5G3UIsTFzbjjWUDlykn557C2 +D4o9T0xQ/6dVFxak75o0MxP4aXGFMZNg/pCBH9DAU7/CKVKRBPAVx1PJ8vIy41MF4p9Mi1 +qmELNvjn9K9fwpaeiUG9qT8B0pfq/tTAObG2sZf7B1mFbA3YwEqjhNqLdkoca5swrS0DI1 +9OejIVIqjK+bvUZaqUDMxVX7fM6hwRvI9Wd+rwFG+X3wHYNPsZ+Cos8/BNPzIIoOh4SbTr +8vIqWUdPXM4HO26uAAVRKz6FknmwM/GQ7FyJBWgIXgXK51SLn+NcifxO8uywGewHzP00ki +frTPmy0+GrikleWry6BdWkg76hjrjQBalNlSmasi9yp9J8qdzVYvQlOBjS5EKWsvsSpXGY +MIdupkiv4a25aXsgdpGBy4GzcSUDioChq287HBBmYRIMVvp5OYbV3/+ERNhTlCQqb6Ck4g +891l1OJOEoMiDqcbDL8DNftlH4gybEE7zJXQRmmJKyw== +License Agreement: CLOVER VERSION 1 (ONE) SOFTWARE LICENSE AGREEMENT + +1. Licenses and Software + +Cortex eBusiness Pty Ltd, an Australian Proprietary Limited Company +("CENQUA") hereby grants to the purchaser (the "LICENSEE") a limited, +revocable, worldwide, non-exclusive, non-transferable, +non-sublicensable license to use the Clover version 1 (one) software +(the "Software"), including any minor upgrades thereof during the Term +(hereinafter defined) up to, but not including the next major version +of the Software. The licensee shall not, or knowingly allow others to, +reverse engineer, decompile, disassemble, modify, adapt, create +derivative works from or otherwise attempt to derive source code from +the Software provided. And, in accordance with the terms and +conditions of this Software License Agreement (the "Agreement"), the +Software shall be used solely by the licensed users in accordance with +the following edition specific conditions: + +a) Server Edition + +A Server Edition license entitles the Licensee to execute one instance +of Clover Server Edition on one (1) machine for the purposes of +instrumenting source code and generating reports. There are no +limitations on the use of the instrumented source code or generated +reports produced by Server Edition. + +b) Workstation Edition + +A Workstation Edition license entitles the licensee to use Clover +Workstation Edition on one (1) machine by one (1) individual end +user. Workstation Edition does not permit the generation of reports +for distribution. + +c) Team Edition + +A Team Edition license entitles the licensee to use Clover Team +edition on any number of machines solely by the licensed number of +users. Reports generated by Clover Team Edition are strictly for use +only by the licensed number of individual end users. + +2. License Fee + +In exchange for the License(s), the Licensee shall pay to Cenqua a +one-time, up front, non-refundable license fee. At the sole discretion +of Cenqua this fee will be waived for non-commercial +projects. Notwithstanding the Licensee's payment of the License Fee, +Cenqua reserves the right to terminate the License if Cenqua discovers +that the Licensee and/or the Licensee's use of the Software is in +breach of this Agreement. + +3. Proprietary Rights + +Cenqua will retain all right, title and interest in and to the +Software, all copies thereof, and Cenqua website(s), software, and +other intellectual property, including, but not limited to, ownership +of all copyrights, look and feel, trademark rights, design rights, +trade secret rights and any and all other intellectual property and +other proprietary rights therein. The Licensee will not directly or +indirectly obtain or attempt to obtain at any time, any right, title +or interest by registration or otherwise in or to the trademarks, +service marks, copyrights, trade names, symbols, logos or designations +or other intellectual property rights owned or used by Cenqua. All +technical manuals or other information provided by Cenqua to the +Licensee shall be the sole property of Cenqua. + +4. Term and Termination + +Subject to the other provisions hereof, this Agreement shall commence +upon the Licensee's opting into this Agreement and continue until the +Licensee discontinues use of the Software or the Agreement terminates +automatically upon the Licensee's breach of any term or condition of +this Agreement (the "Term"). Upon any such termination, the Licensee +will delete the Software immediately. + +5. Copying & Transfer + +The Licensee may copy the Software for back-up purposes only. The +Licensee may not assign or otherwise transfer the Software to any +third party. + +6. Specific Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE IS PROVIDED WITHOUT WARRANTY OF ANY KIND. CENQUA +DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. CENQUA WILL NOT BE LIABLE FOR ANY DAMAGES +ASSOCIATED WITH THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ORDINARY, +INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING +BUT NOT LIMITED TO DAMAGES RELATING TO LOST DATA OR LOST PROFITS, EVEN +IF CENQUA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Warranties and Representations + +Licensee Indemnification. CENQUA agrees to indemnify, defend and hold +the Licensee harmless from and against any and all liabilities, +damages, losses, claims, costs, and expenses (including reasonable +legal fees) arising out of or resulting from the Software or the use +thereof infringing upon, misappropriating or violating any patents, +copyrights, trademarks, or trade secret rights or other proprietary +rights of persons, firms or entities who are not parties to this +Agreement. + +CENQUA Indemnification. The Licensee warrants and represents that the +Licensee's actions with regard to the Software will be in compliance +with all applicable laws; and the Licensee agrees to indemnify, +defend, and hold CENQUA harmless from and against any and all +liabilities, damages, losses, claims, costs, and expenses (including +reasonable legal fees) arising out of or resulting from the +Licensee's failure to observe the use restrictions set forth herein. + +8. Publicity + +The Licensee grants permission for CENQUA to use Licensee's name +solely in customer lists. CENQUA shall not, without prior consent in +writing, use the Licensee's name, or that of its affiliates, in any +form with the specific exception of customer lists. CENQUA agrees to +remove Licensee's name from any and all materials within 7 days if +notified by the Licensee in writing. + +9. Governing Law + +This Agreement shall be governed by the laws of New South Wales, +Australia. + +10. Independent Contractors + +The parties are independent contractors with respect to each other, +and nothing in this Agreement shall be construed as creating an +employer-employee relationship, a partnership, agency relationship or +a joint venture between the parties. + +11. Assignment + +This Agreement is not assignable or transferable by the Licensee. +CENQUA in its sole discretion may transfer a license to a third party +at the written request of the Licensee. + +12. Entire Agreement + +This Agreement constitutes the entire agreement between the parties +concerning the Licensee's use of the Software. This Agreement +supersedes any prior verbal understanding between the parties and any +Licensee purchase order or other ordering document, regardless of +whether such document is received by CENQUA before or after execution +of this Agreement. This Agreement may be amended only in writing by +CENQUA. diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java b/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java new file mode 100644 index 0000000..334510e --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java @@ -0,0 +1,111 @@ +package org.jasig.cas.client; + + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public 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 +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AbstractCasAuthorizedDeciderTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AbstractCasAuthorizedDeciderTests.java new file mode 100644 index 0000000..4ee2957 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AbstractCasAuthorizedDeciderTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +import org.jasig.cas.authentication.principal.SimplePrincipal; + +import junit.framework.TestCase; + +/** + * Abstract test for all CasAuthorizedDecider implementations. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public abstract class AbstractCasAuthorizedDeciderTests extends TestCase { + + private CasAuthorizedDecider casAuthorizedDecider; + + protected abstract CasAuthorizedDecider getCasAuthorizedDeciderImpl(); + + protected void setUp() throws Exception { + this.casAuthorizedDecider = getCasAuthorizedDeciderImpl(); + } + + public void testIsAuthorized() { + assertTrue(this.casAuthorizedDecider + .isAuthorizedToUseApplication(new SimplePrincipal("scott"))); + } + + public void testIsNotAuthorized() { + assertFalse(this.casAuthorizedDecider + .isAuthorizedToUseApplication(new SimplePrincipal("not"))); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AuthorizationExceptionTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AuthorizationExceptionTests.java new file mode 100644 index 0000000..5430499 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/AuthorizationExceptionTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +import junit.framework.TestCase; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class AuthorizationExceptionTests extends TestCase { + + private static final String CONST_MESSAGE = "t"; + + private static final Exception CONST_THROWABLE = new RuntimeException(); + + public void testMessageThrowable() { + final AuthorizationException e = new AuthorizationException( + CONST_MESSAGE, CONST_THROWABLE); + + assertEquals(CONST_MESSAGE, e.getMessage()); + assertEquals(CONST_THROWABLE, e.getCause()); + } + + public void testMessage() { + final AuthorizationException e = new AuthorizationException( + CONST_MESSAGE); + + assertEquals(CONST_MESSAGE, e.getMessage()); + } + + public void testThrowable() { + final AuthorizationException e = new AuthorizationException( + CONST_THROWABLE); + + assertEquals(CONST_THROWABLE, e.getCause()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImplTests.java new file mode 100644 index 0000000..427d7ba --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authorization/DefaultCasAuthorizedDeciderImplTests.java @@ -0,0 +1,23 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.authorization; + +import java.util.ArrayList; +import java.util.List; + +public final class DefaultCasAuthorizedDeciderImplTests extends + AbstractCasAuthorizedDeciderTests { + + public CasAuthorizedDecider getCasAuthorizedDeciderImpl() { + final DefaultCasAuthorizedDeciderImpl impl = new DefaultCasAuthorizedDeciderImpl(); + final List list = new ArrayList(); + list.add("scott"); + impl.setUsers(list); + impl.init(); + return impl; + } + +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/AbstractProxyGrantingTicketStorageTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/AbstractProxyGrantingTicketStorageTests.java new file mode 100644 index 0000000..aca2b4b --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/AbstractProxyGrantingTicketStorageTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import junit.framework.TestCase; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public abstract class AbstractProxyGrantingTicketStorageTests extends TestCase { + + protected ProxyGrantingTicketStorageImpl proxyGrantingTicketStorageImpl; + + public void testNullValue() { + assertNull(this.proxyGrantingTicketStorageImpl + .retrieve("this should not exist")); + } + + public void testValueExists() { + final String CONST_KEY = "testKey"; + final String CONST_VALUE = "testValue"; + + this.proxyGrantingTicketStorageImpl.save(CONST_KEY, CONST_VALUE); + + assertEquals(CONST_VALUE, this.proxyGrantingTicketStorageImpl + .retrieve(CONST_KEY)); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTests.java new file mode 100644 index 0000000..20e686b --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTests.java @@ -0,0 +1,23 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +/** + * Test cases for the ProxyGrantingTicketStorageImplTests. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class ProxyGrantingTicketStorageImplTests extends + AbstractProxyGrantingTicketStorageTests { + + protected void setUp() throws Exception { + this.proxyGrantingTicketStorageImpl = new ProxyGrantingTicketStorageImpl(); + this.proxyGrantingTicketStorageImpl.setTimeout(1000); + this.proxyGrantingTicketStorageImpl.init(); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServletTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServletTests.java new file mode 100644 index 0000000..1784abb --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/SpringConfiguredProxyReceptorServletTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.proxy; + +import junit.framework.TestCase; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletConfig; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.XmlWebApplicationContext; + +import javax.servlet.ServletConfig; + +public class SpringConfiguredProxyReceptorServletTests extends TestCase { + + private XmlWebApplicationContext webApplicationContext; + + public void testNoProxyGrantingTicketStorage() { + AbstractProxyReceptorServlet servlet = new SpringConfiguredProxyReceptorServlet(); + try { + servlet + .init(getServletConfig("classpath:badProxyGrantingTicketStorageConfig.xml")); + fail("Exception expected."); + } catch (final Exception e) { + // expected + } + } + + public void testTwoProxyGrantingTicketStorage() { + AbstractProxyReceptorServlet servlet = new SpringConfiguredProxyReceptorServlet(); + try { + servlet + .init(getServletConfig("classpath:twoProxyGrantingTicketStorageConfig.xml")); + fail("Exception expected."); + } catch (final Exception e) { + // expected + } + } + + public void testOneProxyGrantingTicketStorage() { + AbstractProxyReceptorServlet servlet = new SpringConfiguredProxyReceptorServlet(); + try { + servlet + .init(getServletConfig("classpath:oneProxyGrantingTicketStorageConfig.xml")); + } catch (final Exception e) { + fail("Unexpected excception."); + } + } + + public void testNoPgtOrPgtIouPassed() throws Exception { + final AbstractProxyReceptorServlet servlet = new SpringConfiguredProxyReceptorServlet(); + servlet + .init(getServletConfig("classpath:proxyGrantingTicketStorageConfig.xml")); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.doGet(new MockHttpServletRequest(), response); + + assertEquals("", response.getContentAsString()); + } + + public void testPgtPassed() throws Exception { + final AbstractProxyReceptorServlet servlet = new SpringConfiguredProxyReceptorServlet(); + servlet + .init(getServletConfig("classpath:proxyGrantingTicketStorageConfig.xml")); + + final ProxyGrantingTicketStorage storage = (ProxyGrantingTicketStorage) this.webApplicationContext + .getBean("proxyGrantingTicketStorage"); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + request.setParameter("pgtIou", "test"); + request.setParameter("pgtId", "testpgtId"); + final MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.doGet(request, response); + + assertTrue(!"".equals(response.getContentAsString())); + assertEquals("testpgtId", storage.retrieve("test")); + } + + private ServletConfig getServletConfig(final String contextLocation) { + this.webApplicationContext = new XmlWebApplicationContext(); + this.webApplicationContext + .setConfigLocations(new String[] {contextLocation}); + this.webApplicationContext.refresh(); + + MockServletContext context = new MockServletContext(); + context.setAttribute( + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, + this.webApplicationContext); + + return new MockServletConfig(context); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java new file mode 100644 index 0000000..ee3d5f2 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import java.util.ArrayList; +import java.util.Collection; + +import junit.framework.TestCase; + +/** + * Tests for the CommonUtils. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public 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(" ")); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/DelegatingFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/DelegatingFilterTests.java new file mode 100644 index 0000000..a64b1d3 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/DelegatingFilterTests.java @@ -0,0 +1,143 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.util; + +import junit.framework.TestCase; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import javax.servlet.*; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Test Cases for DelegatingFilter + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class DelegatingFilterTests extends TestCase { + + private DelegatingFilter delegatingFilter; + + protected int filterExecuted = -1; + + protected void setUp() throws Exception { + final Map delegators = new HashMap(); + + delegators.put("1", new TestFilter(1)); + + this.filterExecuted = -1; + this.delegatingFilter = new DelegatingFilter(); + this.delegatingFilter.setDefaultFilter(new TestFilter(0)); + this.delegatingFilter.setExactMatch(true); + this.delegatingFilter.setDelegators(delegators); + this.delegatingFilter.setRequestParameterName("test"); + this.delegatingFilter.init(); + this.delegatingFilter.init(null); + } + + protected void tearDown() throws Exception { + this.delegatingFilter.destroy(); + } + + public void testExactMatchFound() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("test", "1"); + + this.delegatingFilter.doFilter(request, new MockHttpServletResponse(), + null); + + assertEquals(1, this.filterExecuted); + } + + public void testNoMatchFound() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("test", "0"); + + this.delegatingFilter.doFilter(request, new MockHttpServletResponse(), + null); + + assertEquals(0, this.filterExecuted); + } + + public void testNoParam() throws Exception { + this.delegatingFilter.doFilter(new MockHttpServletRequest(), + new MockHttpServletResponse(), null); + + assertEquals(0, this.filterExecuted); + } + + public void testRegularExpressionMatch() throws Exception { + + final Map delegators = new HashMap(); + + delegators.put("1.*", new TestFilter(1)); + + this.delegatingFilter.setExactMatch(false); + this.delegatingFilter.setDelegators(delegators); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("test", "1"); + + this.delegatingFilter.doFilter(request, new MockHttpServletResponse(), + null); + + assertEquals(1, this.filterExecuted); + request = new MockHttpServletRequest(); + request.addParameter("test", "15"); + + this.delegatingFilter.doFilter(request, new MockHttpServletResponse(), + null); + + assertEquals(1, this.filterExecuted); + request = new MockHttpServletRequest(); + request.addParameter("test", "0"); + + this.delegatingFilter.doFilter(request, new MockHttpServletResponse(), + null); + + assertEquals(0, this.filterExecuted); + } + + public void testForIllegalArgument() { + Map map = new HashMap(); + map.put("test", new Object()); + + this.delegatingFilter.setDelegators(map); + + try { + this.delegatingFilter.init(); + fail("Exception expected."); + } catch (IllegalArgumentException e) { + // expected + } + } + + private class TestFilter implements Filter { + + private final int i; + + public TestFilter(final int i) { + this.i = i; + } + + public void destroy() { + // nothing to do here + } + + public void doFilter(ServletRequest arg0, ServletResponse arg1, + FilterChain arg2) throws IOException, ServletException { + DelegatingFilterTests.this.filterExecuted = this.i; + + } + + public void init(FilterConfig arg0) throws ServletException { + // nothing to do + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java new file mode 100644 index 0000000..2b5d242 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java @@ -0,0 +1,22 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.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$ $Date$ + * @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"; +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java new file mode 100644 index 0000000..1b33057 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.authentication.principal.SimplePrincipal; + +import java.util.HashMap; +import java.util.Map; + +/** + * Test cases for the {@link AssertionImpl}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class AssertionImplTests extends TestCase { + + private static final Principal CONST_PRINCIPAL = new SimplePrincipal("test"); + + private static final String CONST_PROXY_GRANTING_TICKET_IOU = "proxyGrantingTicketIou"; + + 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.getProxyGrantingTicketId()); + } + + public void testCompleteConstructor() { + final Assertion assertion = new AssertionImpl(CONST_PRINCIPAL, + CONST_ATTRIBUTES, CONST_PROXY_GRANTING_TICKET_IOU); + + assertEquals(CONST_PRINCIPAL, assertion.getPrincipal()); + assertEquals(CONST_ATTRIBUTES, assertion.getAttributes()); + assertEquals(CONST_PROXY_GRANTING_TICKET_IOU, assertion + .getProxyGrantingTicketId()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java new file mode 100644 index 0000000..1604b75 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + + +import org.apache.commons.httpclient.HttpClient; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.PublicTestHttpServer; + +import java.io.UnsupportedEncodingException; + +/** + * Test cases for the {@link Cas10TicketValidator}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class Cas10TicketValidatorTests extends AbstractTicketValidatorTests { + + private Cas10TicketValidator ticketValidator; + + public Cas10TicketValidatorTests() { + super(); + } + + protected void setUp() throws Exception { + this.ticketValidator = new Cas10TicketValidator(); + this.ticketValidator.setCasServerUrl(CONST_CAS_SERVER_URL); + this.ticketValidator.setRenew(true); + this.ticketValidator.setHttpClient(new HttpClient()); + this.ticketValidator.init(); + } + + public void testNoResponse() throws Exception { + PublicTestHttpServer.instance().content = "no\n\n" + .getBytes(PublicTestHttpServer.instance().encoding); + try { + this.ticketValidator.validate("testTicket", new SimpleService( + "myService")); + fail("ValidationException expected."); + } catch (final ValidationException e) { + // expected + } + } + + public void testYesResponse() throws ValidationException, + UnsupportedEncodingException { + PublicTestHttpServer.instance().content = "yes\nusername\n\n" + .getBytes(PublicTestHttpServer.instance().encoding); + final Assertion assertion = this.ticketValidator.validate("testTicket", + new SimpleService("myService")); + assertEquals(CONST_USERNAME, assertion.getPrincipal().getId()); + } + + public void testBadResponse() throws UnsupportedEncodingException { + PublicTestHttpServer.instance().content = "falalala\n\n" + .getBytes(PublicTestHttpServer.instance().encoding); + try { + this.ticketValidator.validate("testTicket", new SimpleService( + "myService")); + fail("ValidationException expected."); + } catch (final ValidationException e) { + // expected + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java new file mode 100644 index 0000000..99e7129 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + + +import org.apache.commons.httpclient.HttpClient; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.PublicTestHttpServer; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +/** + * Test cases for the {@link Cas20ProxyTicketValidator}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class Cas20ProxyTicketValidatorTests extends + AbstractTicketValidatorTests { + + private Cas20ProxyTicketValidator ticketValidator; + + public Cas20ProxyTicketValidatorTests() { + super(); + } + + protected void setUp() throws Exception { + ProxyGrantingTicketStorage proxyGrantingTicketStorage = getProxyGrantingTicketStorage(); + this.ticketValidator = new Cas20ProxyTicketValidator(); + this.ticketValidator.setCasServerUrl(CONST_CAS_SERVER_URL); + this.ticketValidator.setRenew(true); + this.ticketValidator + .setProxyGrantingTicketStorage(proxyGrantingTicketStorage); + this.ticketValidator.setHttpClient(new HttpClient()); + + final List list = new ArrayList(); + list.add("proxy1 proxy2 proxy3"); + this.ticketValidator.setProxyChains(list); + + this.ticketValidator.init(); + } + + private ProxyGrantingTicketStorage getProxyGrantingTicketStorage() + throws Exception { + ProxyGrantingTicketStorageImpl proxyGrantingTicketStorageImpl = new ProxyGrantingTicketStorageImpl(); + proxyGrantingTicketStorageImpl.init(); + + return proxyGrantingTicketStorageImpl; + } + + public void testProxyChainWithValidProxy() throws ValidationException, + UnsupportedEncodingException { + final String USERNAME = "username"; + final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy1proxy2proxy3"; + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + + final Assertion assertion = this.ticketValidator.validate("test", + new SimpleService("test")); + assertEquals(USERNAME, assertion.getPrincipal().getId()); + } + + public void testProxyChainWithInvalidProxy() throws ValidationException, + UnsupportedEncodingException { + final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy7proxy2proxy3"; + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + + try { + this.ticketValidator.validate("test", new SimpleService("test")); + fail("Invalid proxy chain"); + } catch (InvalidProxyChainValidationException e) { + // expected + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java new file mode 100644 index 0000000..c8eb98b --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java @@ -0,0 +1,113 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.validation; + + +import org.apache.commons.httpclient.HttpClient; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.PublicTestHttpServer; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; + +import java.io.UnsupportedEncodingException; + +/** + * Test cases for the {@link Cas20ServiceTicketValidator}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class Cas20ServiceTicketValidatorTests extends + AbstractTicketValidatorTests { + + private Cas20ServiceTicketValidator ticketValidator; + + private ProxyGrantingTicketStorage proxyGrantingTicketStorage; + + public Cas20ServiceTicketValidatorTests() { + super(); + } + + protected void setUp() throws Exception { + this.proxyGrantingTicketStorage = getProxyGrantingTicketStorage(); + this.ticketValidator = new Cas20ServiceTicketValidator(); + this.ticketValidator.setCasServerUrl(CONST_CAS_SERVER_URL); + this.ticketValidator.setRenew(true); + this.ticketValidator + .setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage); + this.ticketValidator.setHttpClient(new HttpClient()); + this.ticketValidator.init(); + } + + private ProxyGrantingTicketStorage getProxyGrantingTicketStorage() + throws Exception { + ProxyGrantingTicketStorageImpl proxyGrantingTicketStorageImpl = new ProxyGrantingTicketStorageImpl(); + proxyGrantingTicketStorageImpl.init(); + + return proxyGrantingTicketStorageImpl; + } + + public void testNoResponse() throws UnsupportedEncodingException { + final String RESPONSE = "Ticket ST-1856339-aA5Yuvrxzpv8Tau1cYQ7 not recognized"; + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + try { + this.ticketValidator.validate("test", new SimpleService("test")); + fail("ValidationException expected due to 'no' response"); + } catch (final ValidationException e) { + // expected + } + } + + public void testYesResponseButNoPgt() throws ValidationException, + UnsupportedEncodingException { + final String USERNAME = "username"; + final String RESPONSE = "" + + USERNAME + + ""; + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + + final Assertion assertion = this.ticketValidator.validate("test", + new SimpleService("test")); + assertEquals(USERNAME, assertion.getPrincipal().getId()); + } + + public void testYesResponseWithPgt() throws ValidationException, + UnsupportedEncodingException { + final String USERNAME = "username"; + final String PGTIOU = "testPgtIou"; + final String PGT = "test"; + final String RESPONSE = "" + + USERNAME + + "" + + PGTIOU + + ""; + + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + + this.proxyGrantingTicketStorage.save(PGTIOU, PGT); + + final Assertion assertion = this.ticketValidator.validate("test", + new SimpleService("test")); + assertEquals(USERNAME, assertion.getPrincipal().getId()); + assertEquals(PGT, assertion.getProxyGrantingTicketId()); + } + + public void testInvalidResponse() throws Exception { + final String RESPONSE = ""; + PublicTestHttpServer.instance().content = RESPONSE + .getBytes(PublicTestHttpServer.instance().encoding); + try { + this.ticketValidator.validate("test", new SimpleService("test")); + fail("ValidationException expected due to invalid response."); + } catch (final ValidationException e) { + // expected + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthenticationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthenticationFilterTests.java new file mode 100644 index 0000000..a4dba6f --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthenticationFilterTests.java @@ -0,0 +1,162 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.SimplePrincipal; +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 CasAuthenticationFilter. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasAuthenticationFilterTests 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 CasAuthenticationFilter filter; + + protected void setUp() throws Exception { + this.filter = new CasAuthenticationFilter(); + this.filter.setCasServerLoginUrl(CAS_LOGIN_URL); + this.filter.setGateway(false); + this.filter.setRenew(false); + this.filter.setServiceUrl(CAS_SERVICE_URL); + this.filter.init(new MockFilterConfig()); + this.filter.init(); + } + + 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.setServiceUrl(null); + this.filter.setServerName("localhost:8443"); + 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_ASSERTION, + new AssertionImpl(new SimplePrincipal("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.setGateway(true); + this.filter.doFilter(request, response, filterChain); + assertNotNull(session.getAttribute(AbstractCasFilter.CONST_GATEWAY)); + assertNotNull(response.getRedirectedUrl()); + + final MockHttpServletResponse response2 = new MockHttpServletResponse(); + this.filter.doFilter(request, response2, filterChain); + assertNull(session.getAttribute(AbstractCasFilter.CONST_GATEWAY)); + assertNull(response2.getRedirectedUrl()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthorizationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthorizationFilterTests.java new file mode 100644 index 0000000..fe0ed5e --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasAuthorizationFilterTests.java @@ -0,0 +1,110 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Principal; +import org.jasig.cas.authentication.principal.SimplePrincipal; +import org.jasig.cas.client.authorization.AuthorizationException; +import org.jasig.cas.client.authorization.CasAuthorizedDecider; +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; + +/** + * Tests for the CasAuthorizationFilter. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasAuthorizationFilterTests extends TestCase { + + private CasAuthorizationFilter casAuthorizationFilter; + + protected void setUp() throws Exception { + this.casAuthorizationFilter = new CasAuthorizationFilter(); + this.casAuthorizationFilter.setDecider(new CasAuthorizedDecider(){ + + public boolean isAuthorizedToUseApplication(Principal principal) { + return principal.getId().equals("scott"); + } + }); + this.casAuthorizationFilter.init(); + this.casAuthorizationFilter.init(new MockFilterConfig()); + } + + protected void tearDown() throws Exception { + this.casAuthorizationFilter.destroy(); + } + + public void testSuccesfulAuthorization() { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + session.setAttribute(AbstractCasFilter.CONST_ASSERTION, + new AssertionImpl(new SimplePrincipal("scott"))); + + try { + this.casAuthorizationFilter.doFilter(request, response, + new FilterChain(){ + + public void doFilter(ServletRequest arg0, + ServletResponse arg1) throws IOException, + ServletException { + // nothing to do + } + }); + } catch (Exception e) { + fail("No exception expected"); + } + } + + public void testFailedAuthorization() { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + session.setAttribute(AbstractCasFilter.CONST_ASSERTION, + new AssertionImpl(new SimplePrincipal("test"))); + + try { + this.casAuthorizationFilter.doFilter(request, response, null); + fail("ServletException expected."); + } catch (AuthorizationException e) { + // expectd + } catch (Exception e) { + fail("AuthorizationException expected, not IOException."); + } + } + + public void testNoAssertionFound() { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + try { + this.casAuthorizationFilter.doFilter(request, response, null); + fail("ServletException expected."); + } catch (ServletException e) { + // expected + } catch (Exception e) { + fail("ServletException expected, not IOException."); + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasValidationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasValidationFilterTests.java new file mode 100644 index 0000000..eeb7090 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/CasValidationFilterTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimplePrincipal; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.AssertionImpl; +import org.jasig.cas.client.validation.TicketValidator; +import org.jasig.cas.client.validation.ValidationException; +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; + +/** + * Tests for the CasValidationFilter. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasValidationFilterTests extends TestCase { + + private CasValidationFilter filter; + + protected void setUp() throws Exception { + this.filter = new CasValidationFilter(); + this.filter.setServerName("https://localhost"); + this.filter.setTicketValidator(new TicketValidator(){ + + public Assertion validate(final String ticketId, + final Service service) throws ValidationException { + if (ticketId.equals("true")) { + return new AssertionImpl(new SimplePrincipal("test")); + } + throw new ValidationException("error validating ticket."); + } + }); + this.filter.init(); + } + + protected void tearDown() throws Exception { + this.filter.destroy(); + } + + public void testNoTicket() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + final FilterChain filterChain = new FilterChain(){ + + public void doFilter(final ServletRequest arg0, + final ServletResponse arg1) throws IOException, + ServletException { + // nothing to do + } + }; + + this.filter.doFilter(request, response, filterChain); + + assertNull(session.getAttribute(AbstractCasFilter.CONST_ASSERTION)); + } + + public void testValidationSuccess() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + request.setParameter(AbstractCasFilter.PARAM_TICKET, "true"); + final FilterChain filterChain = new FilterChain(){ + + public void doFilter(final ServletRequest arg0, + final ServletResponse arg1) throws IOException, + ServletException { + // nothing to do + } + }; + + this.filter.doFilter(request, response, filterChain); + + assertNotNull(session.getAttribute(AbstractCasFilter.CONST_ASSERTION)); + } + + public void testValidationFailure() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + request.setParameter(AbstractCasFilter.PARAM_TICKET, "false"); + final FilterChain filterChain = new FilterChain(){ + + public void doFilter(final ServletRequest arg0, + final ServletResponse arg1) throws IOException, + ServletException { + // nothing to do + } + }; + + try { + this.filter.doFilter(request, response, filterChain); + fail("Exception expected."); + } catch (final ServletException e) { + assertTrue(e.getRootCause().getClass().isAssignableFrom( + ValidationException.class)); + // expected + } + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilterTests.java new file mode 100644 index 0000000..f267378 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/web/filter/HttpServletRequestWrapperFilterTests.java @@ -0,0 +1,155 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.web.filter; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.SimplePrincipal; +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$ $Date$ + * @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_ASSERTION, + new AssertionImpl(new SimplePrincipal("test"))); + + request.setSession(session); + + this.filter.doFilter(request, new MockHttpServletResponse(), + filterChain); + assertEquals("test", this.mockRequest.getRemoteUser()); + assertEquals(request.getAttributeNames(), this.mockRequest + .getAttributeNames()); + assertEquals(request.getAuthType(), this.mockRequest.getAuthType()); + + this.mockRequest.setCharacterEncoding("test"); + + assertEquals(request.getCharacterEncoding(), this.mockRequest + .getCharacterEncoding()); + assertNotSame(request.getClass(), this.mockRequest.getClass()); + assertEquals(request.getContentLength(), this.mockRequest + .getContentLength()); + assertEquals(request.getContentType(), this.mockRequest + .getContentType()); + assertEquals(request.getContextPath(), this.mockRequest + .getContextPath()); + assertEquals(request.getCookies(), this.mockRequest.getCookies()); + assertEquals(request.getHeaderNames(), this.mockRequest + .getHeaderNames()); + assertEquals(request.getInputStream(), this.mockRequest + .getInputStream()); + assertEquals(request.getLocalAddr(), this.mockRequest.getLocalAddr()); + assertEquals(request.getLocale(), this.mockRequest.getLocale()); + assertEquals(request.getLocales().hasMoreElements(), this.mockRequest + .getLocales().hasMoreElements()); + assertEquals(request.getLocalName(), this.mockRequest.getLocalName()); + assertEquals(request.getLocalPort(), this.mockRequest.getLocalPort()); + assertEquals(request.getMethod(), this.mockRequest.getMethod()); + assertEquals(request.getParameterMap(), this.mockRequest + .getParameterMap()); + assertEquals(request.getParameterNames().hasMoreElements(), + this.mockRequest.getParameterNames().hasMoreElements()); + assertEquals(request.getPathInfo(), this.mockRequest.getPathInfo()); + assertEquals(request.getPathTranslated(), this.mockRequest + .getPathTranslated()); + assertEquals(request.getProtocol(), this.mockRequest.getProtocol()); + assertEquals(request.getQueryString(), this.mockRequest + .getQueryString()); + assertEquals(request.getReader(), this.mockRequest.getReader()); + assertEquals(request.getRemoteAddr(), this.mockRequest.getRemoteAddr()); + assertEquals(request.getRemoteHost(), this.mockRequest.getRemoteHost()); + assertEquals(request.getRemotePort(), this.mockRequest.getRemotePort()); + assertEquals(request.getRequestedSessionId(), this.mockRequest + .getRequestedSessionId()); + assertEquals(request.getRequestURI(), this.mockRequest.getRequestURI()); + assertEquals(request.getRequestURL().toString(), this.mockRequest + .getRequestURL().toString()); + assertEquals(request.getScheme(), this.mockRequest.getScheme()); + assertEquals(request.getServerName(), this.mockRequest.getServerName()); + assertEquals(request.getServerPort(), this.mockRequest.getServerPort()); + assertEquals(request.getServletPath(), this.mockRequest + .getServletPath()); + assertEquals(request.getSession(), this.mockRequest.getSession()); + assertEquals(request.getSession(false), this.mockRequest + .getSession(false)); + assertEquals(request.getUserPrincipal(), this.mockRequest + .getUserPrincipal()); + assertEquals(request.isRequestedSessionIdFromCookie(), this.mockRequest + .isRequestedSessionIdFromCookie()); + assertEquals(request.isRequestedSessionIdFromUrl(), this.mockRequest + .isRequestedSessionIdFromUrl()); + assertEquals(request.isRequestedSessionIdFromURL(), this.mockRequest + .isRequestedSessionIdFromURL()); + assertEquals(request.isRequestedSessionIdValid(), this.mockRequest + .isRequestedSessionIdValid()); + assertEquals(request.isSecure(), this.mockRequest.isSecure()); + assertEquals(request.isUserInRole("test"), this.mockRequest + .isUserInRole("test")); + assertEquals(request.getDateHeader("test"), this.mockRequest + .getDateHeader("test")); + assertEquals(request.getHeader("test"), this.mockRequest + .getHeader("test")); + assertEquals(request.getHeaders("test").hasMoreElements(), + this.mockRequest.getHeaders("test").hasMoreElements()); + assertEquals(request.getIntHeader("test"), this.mockRequest + .getIntHeader("test")); + + this.mockRequest.setAttribute("test", "test"); + + assertEquals(request.getAttribute("test"), this.mockRequest + .getAttribute("test")); + + this.mockRequest.removeAttribute("test"); + + assertEquals(request.getAttribute("test"), this.mockRequest + .getAttribute("test")); + assertEquals(request.getParameter("test"), this.mockRequest + .getParameter("test")); + assertEquals(request.getParameterValues("test"), this.mockRequest + .getParameterValues("test")); + assertEquals(request.getRealPath("test"), this.mockRequest + .getRealPath("test")); + assertEquals(request.getRequestDispatcher("test").getClass(), + this.mockRequest.getRequestDispatcher("test").getClass()); + + } +} diff --git a/cas-client-core/src/test/resources/badProxyGrantingTicketStorageConfig.xml b/cas-client-core/src/test/resources/badProxyGrantingTicketStorageConfig.xml new file mode 100644 index 0000000..2e8ef16 --- /dev/null +++ b/cas-client-core/src/test/resources/badProxyGrantingTicketStorageConfig.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/cas-client-core/src/test/resources/oneProxyGrantingTicketStorageConfig.xml b/cas-client-core/src/test/resources/oneProxyGrantingTicketStorageConfig.xml new file mode 100644 index 0000000..c6bcf86 --- /dev/null +++ b/cas-client-core/src/test/resources/oneProxyGrantingTicketStorageConfig.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/cas-client-core/src/test/resources/proxyGrantingTicketStorageConfig.xml b/cas-client-core/src/test/resources/proxyGrantingTicketStorageConfig.xml new file mode 100644 index 0000000..8ed6a2c --- /dev/null +++ b/cas-client-core/src/test/resources/proxyGrantingTicketStorageConfig.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/cas-client-core/src/test/resources/twoProxyGrantingTicketStorageConfig.xml b/cas-client-core/src/test/resources/twoProxyGrantingTicketStorageConfig.xml new file mode 100644 index 0000000..af49ab8 --- /dev/null +++ b/cas-client-core/src/test/resources/twoProxyGrantingTicketStorageConfig.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/cas-client-uportal/.classpath b/cas-client-uportal/.classpath new file mode 100644 index 0000000..e23cd59 --- /dev/null +++ b/cas-client-uportal/.classpath @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cas-client-uportal/.cvsignore b/cas-client-uportal/.cvsignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/cas-client-uportal/.cvsignore @@ -0,0 +1 @@ +target diff --git a/cas-client-uportal/.project b/cas-client-uportal/.project new file mode 100644 index 0000000..9eab0c0 --- /dev/null +++ b/cas-client-uportal/.project @@ -0,0 +1,17 @@ + + cas-client-uportal + 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. + + cas-client-core + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.jdt.core.javanature + + \ No newline at end of file diff --git a/cas-client-uportal/.settings/org.eclipse.jdt.core.prefs b/cas-client-uportal/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..57e31ff --- /dev/null +++ b/cas-client-uportal/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +#Mon Aug 07 13:41:21 EDT 2006 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.source=1.4 +org.eclipse.jdt.core.compiler.compliance=1.4 diff --git a/cas-client-uportal/LICENSE.txt b/cas-client-uportal/LICENSE.txt new file mode 100644 index 0000000..30c8573 --- /dev/null +++ b/cas-client-uportal/LICENSE.txt @@ -0,0 +1,29 @@ +License for Use + +Copyright (c) 2000 The JA-SIG Collaborative. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. 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. + + 3. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the JA-SIG Collaborative + (http://www.ja-sig.org/)." + +This software is provided by the JA-SIG collaborative "as is" and any expressed +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 JA-SIG collaborative or its 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. \ No newline at end of file diff --git a/cas-client-uportal/README.txt b/cas-client-uportal/README.txt new file mode 100644 index 0000000..f5cd476 --- /dev/null +++ b/cas-client-uportal/README.txt @@ -0,0 +1,28 @@ +CENTRAL AUTHENTICATION SERVICE (CAS) +-------------------------------------------------------------------- +http://www.ja-sig.org/products/cas/ + +1. INTRODUCTION + +The Central Authentication Service (CAS) is the standard mechanism by which web +applications should authenticate users. Any custom applications written benefit +from using CAS. + +Note that CAS provides authentication; that is, it determines that your users +are who they say they are. CAS should not be viewed as an access-control system; +in particular, providers of applications that grant access to anyone who +possesses a NetID should understand that loose affiliates of an organization may +be granted NetIDs. + +The JA-SIG CAS Client for Java is a support library for Java applications to communicate +with the CAS server. + +2. RELEASE INFO + +CAS requires J2SE 1.4 and J2EE1.3. + +Release conents: +* "src/main/java" contains the Java source files for the framework +* "src/test/java" contains the Java source files for CAS's test suite + + diff --git a/cas-client-uportal/cas-client-uportal.iml b/cas-client-uportal/cas-client-uportal.iml new file mode 100644 index 0000000..375f369 --- /dev/null +++ b/cas-client-uportal/cas-client-uportal.iml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cas-client-uportal/pom.xml b/cas-client-uportal/pom.xml new file mode 100644 index 0000000..c74a97d --- /dev/null +++ b/cas-client-uportal/pom.xml @@ -0,0 +1,114 @@ + + + org.jasig.cas + cas-client + 3.0-SNAPSHOT + + 4.0.0 + org.jasig.cas + cas-client-uportal + jar + 3.0-SNAPSHOT + JA-SIG CAS Client for Java - uPortal Integration + http://www.ja-sig.org/products/cas/ + + + src/main/java + src/test/java + + + src/test/resources + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.4 + 1.4 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Tests* + + + + + org.apache.maven.plugins + maven-clover-plugin + + ${basedir}/src/test/clover/clover.license + + + + pre-site + + instrument + + + + + + + + + + junit + junit + 3.8.1 + test + + + jasig + uportal + 2.5.3-rc1 + + + org.jasig.cas + cas-client-core + 3.0-SNAPSHOT + + + + false + + + org.apache.maven.plugins + maven-clover-plugin + + ${basedir}/src/test/clover/clover.license + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + 128m + 512m + + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.apache.maven.plugins + maven-surefire-report-plugin + + + org.codehaus.mojo + jdepend-maven-plugin + + + + diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/AbstractCasSecurityContextFactory.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/AbstractCasSecurityContextFactory.java new file mode 100644 index 0000000..7b7e645 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/AbstractCasSecurityContextFactory.java @@ -0,0 +1,80 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.proxy.ProxyRetriever; +import org.jasig.cas.client.validation.TicketValidator; +import org.jasig.portal.security.InitialSecurityContextFactory; +import org.jasig.portal.spring.PortalApplicationContextFacade; + +/** + * Abstract implementation of a SecurityContextFactory that can load all the dependences if a + * CasSecurityCcontext. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + * + */ +public abstract class AbstractCasSecurityContextFactory extends + InitialSecurityContextFactory { + + + /** Spring Bean ID for the Ticket Validator. */ + public static final String CONST_CAS_TICKET_VALIDATOR = "casTicketValidator"; + + /** Spring Bean ID for the Proxy Retriever. */ + public static final String CONST_CAS_PROXY_RETRIEVER = "casProxyRetriever"; + + /** Spring Bean ID for the Service. */ + public static final String CONST_CAS_SERVICE = "casService"; + + /** Spring Bean ID for the ProxyGrantingTicketStorage. */ + public static final String CONST_CAS_PROXY_GRANTING_TICKET_STORAGE = "casProxyGrantingTicketStorage"; + + /** Instance of Commons Logging. */ + protected final Log log = LogFactory.getLog(this.getClass()); + + /** + * The Ticket Validator referenced by the constant + * CONST_CAS_TICKET_VALIDATOR. + */ + protected TicketValidator ticketValidator; + + /** + * The ProxyRetriever referenced by the constant + * CONST_CAS_PROXY_RETRIEVER. + */ + protected ProxyRetriever proxyRetriever; + + /** The Service referenced by the constant CONST_CAS_SERVICE. */ + protected Service service; + + /** + * Default constructor retrieves and caches results from looking up entries + * in the PortalApplicationContext for the Ticket Validator, Proxy Retriever + * and Service. + */ + public AbstractCasSecurityContextFactory() { + this.ticketValidator = (TicketValidator) PortalApplicationContextFacade + .getPortalApplicationContext().getBean(CONST_CAS_TICKET_VALIDATOR); + if (PortalApplicationContextFacade.getPortalApplicationContext() + .containsBean(CONST_CAS_PROXY_RETRIEVER)) { + this.proxyRetriever = (ProxyRetriever) PortalApplicationContextFacade + .getPortalApplicationContext().getBean( + CONST_CAS_PROXY_RETRIEVER); + } else { + log + .warn("No Proxy Retriever found in PortalApplicationFacade. No Proxying capabilities will be provided by CAS."); + } + this.service = (Service) PortalApplicationContextFacade + .getPortalApplicationContext().getBean(CONST_CAS_SERVICE); + } + +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasConnectionContext.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasConnectionContext.java new file mode 100644 index 0000000..1dff5f7 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasConnectionContext.java @@ -0,0 +1,84 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.portal.ChannelRuntimeData; +import org.jasig.portal.ChannelStaticData; +import org.jasig.portal.security.ISecurityContext; +import org.jasig.portal.security.LocalConnectionContext; + +import java.util.Enumeration; + +/** + * Extension to LocalConnectionContext that will retrieve and append a proxy + * ticket to a given descriptor. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasConnectionContext extends LocalConnectionContext { + + /** Instance of ICasSecurityContext. */ + private ICasSecurityContext casSecurityContext; + + public String getDescriptor(String descriptor, + final ChannelRuntimeData channelRuntimeData) { + if (log.isTraceEnabled()) { + log.trace("getDescriptor(" + descriptor + ", " + channelRuntimeData + + ")"); + } + + descriptor = descriptor == null ? "null" : descriptor; + + if (channelRuntimeData.getHttpRequestMethod().equals("GET")) { + + if (this.casSecurityContext != null) { + final String proxyTicket = this.casSecurityContext + .getProxyTicket(new SimpleService(descriptor)); + + if (proxyTicket != null) { + // append ticket parameter and value to query string + if (descriptor.indexOf("?") != -1) { + descriptor = descriptor + "&ticket=" + proxyTicket; + } else { + descriptor = descriptor + "?ticket=" + proxyTicket; + } + } + } + } + + return descriptor; + } + + public void init(final ChannelStaticData channelStaticData) { + final ISecurityContext securityContext = channelStaticData.getPerson() + .getSecurityContext(); + + if (ICasSecurityContext.class.isAssignableFrom(securityContext + .getClass()) + && securityContext.isAuthenticated()) { + this.casSecurityContext = (ICasSecurityContext) securityContext; + } + + final Enumeration enumeration = securityContext.getSubContexts(); + + while (enumeration.hasMoreElements()) { + final ISecurityContext context = (ISecurityContext) enumeration + .nextElement(); + + if (ISecurityContext.class.isAssignableFrom(context.getClass()) + && context.isAuthenticated()) { + this.casSecurityContext = (ICasSecurityContext) context; + } + } + + if (this.casSecurityContext == null) { + log.warn("Unable to find authenticated ICasSecurityContext"); + } + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContext.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContext.java new file mode 100644 index 0000000..68728d3 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContext.java @@ -0,0 +1,99 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.proxy.ProxyRetriever; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.TicketValidator; +import org.jasig.cas.client.validation.ValidationException; +import org.jasig.portal.security.PortalSecurityException; +import org.jasig.portal.security.provider.ChainingSecurityContext; + +/** + * Implementation of ICasSecurityContext that knows how to handle CAS ticket + * validation, as well as the retrieval of Proxy Tickets. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class CasSecurityContext extends ChainingSecurityContext implements + ICasSecurityContext { + + /** Unique Id for Serialization */ + private static final long serialVersionUID = 1L; + + /** Instance of TicketValidator to validate tickets. */ + private final TicketValidator ticketValidator; + + /** Instance of ProxyRetriever to obtain proxy tickets. */ + private final ProxyRetriever proxyRetriever; + + /** Instance of Service representing uPortal instance. */ + private final Service service; + + /** Assertion about the person this security context is for. */ + private Assertion assertion; + + /** + * Instantiate a new CasSecurityContext, setting the required fields. + * + */ + public CasSecurityContext(final TicketValidator ticketValidator, final Service service, final ProxyRetriever proxyRetriever + ) { + CommonUtils.assertNotNull(ticketValidator, "ticketValidator cannot be null."); + CommonUtils.assertNotNull(service, "service cannot be null."); + + log.trace("Initalizing CasSecurityContext"); + this.ticketValidator = ticketValidator; + this.service = service; + this.proxyRetriever = proxyRetriever; + } + + public final String getProxyTicket(final Service service) { + if (this.proxyRetriever == null + || CommonUtils.isEmpty(this.assertion.getProxyGrantingTicketId())) { + return null; + } + + return this.proxyRetriever.getProxyTicketIdFor(this.assertion + .getProxyGrantingTicketId(), service); + } + + public final int getAuthType() { + return ICasSecurityContext.CAS_AUTHTYPE; + } + + public final synchronized void authenticate() throws PortalSecurityException { + this.isauth = false; + final String serviceTicket = new String( + this.myOpaqueCredentials.credentialstring); + final Service service = getService(); + + if (log.isDebugEnabled()) { + log.debug("Attempting to validate ticket [" + serviceTicket + + "] for service [" + service.toString()); + } + + try { + this.assertion = this.ticketValidator.validate(serviceTicket, + service); + this.myAdditionalDescriptor = null; + this.myPrincipal.setUID(this.assertion.getPrincipal().getId()); + this.isauth = true; + super.authenticate(); + } catch (final ValidationException e) { + log.warn(e, e); + throw new PortalSecurityException(e.getMessage(), e); + } + } + + protected Service getService() { + return this.service; + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactory.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactory.java new file mode 100644 index 0000000..29a4510 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.portal.security.ISecurityContext; +import org.jasig.portal.security.ISecurityContextFactory; + +/** + * Implementation of an {@link ISecurityContextFactory} that on creation will + * retrieve references to Spring managed CAS client objects and pass them to all + * new CasSecurityContexts that are created. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class CasSecurityContextFactory extends AbstractCasSecurityContextFactory { + + /** + * Instantiate a new instance of CasSecurityContext. + */ + public ISecurityContext getSecurityContext() { + log + .trace("Returning CasSecurityContext from CasSecurityContextFactory."); + return new CasSecurityContext(this.ticketValidator, this.service, this.proxyRetriever + ); + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ICasSecurityContext.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ICasSecurityContext.java new file mode 100644 index 0000000..ba7dae9 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ICasSecurityContext.java @@ -0,0 +1,30 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.Service; + +/** + * Interface implemented by CAS security contexts. These implementations are + * aware of proxying, and can retrieve a ticket from CAS for accessing a + * specific service. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public interface ICasSecurityContext { + + /** Authentication type for CAS authentication */ + public static final int CAS_AUTHTYPE = 0x1701; + + /** Retrieve a Proxy Ticket Id for a particular service we wish to proxy against. + * + * @param service the service to retrieve a proxy ticket for. + * @return the ticket id, or null if no ticket could be retrieved. + */ + String getProxyTicket(Service service); +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ServiceHolder.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ServiceHolder.java new file mode 100644 index 0000000..043f624 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ServiceHolder.java @@ -0,0 +1,38 @@ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.util.CommonUtils; + +/** + * ThreadLocal container that exposes the service to lower layers in the authentication stack. Because of the ISecurityCcontext + * API this service url is not normally available. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class ServiceHolder { + + private static final ThreadLocal threadLocal = new ThreadLocal(); + + public static void setService(final Service service) { + CommonUtils.assertNotNull(service, "service cannot be null."); + threadLocal.set(service); + } + + /** + * Method to retrieve the service from the ThreadLocal + * + * @return the service. Should not ever be null. + */ + public static Service getService() { + return (Service) threadLocal.get(); + } + + /** + * Reset the context to clear it out. + */ + public static void clearContext() { + threadLocal.set(null); + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContext.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContext.java new file mode 100644 index 0000000..437de4a --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.client.proxy.ProxyRetriever; +import org.jasig.cas.client.validation.TicketValidator; + +/** + * Extension of AbstractCasSecurityContext that retrieves the Service from the + * ServiceHolder ThreadLocal object. This allows for a more flexible service to + * be provided for ticket validation. This is needed as the normal + * ISecurityContext has no mechanism for service urls based on requests. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class ThreadLocalAwareCasSecurityContext extends + CasSecurityContext { + + /** Unique Id for Serialization. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiate a new CasSecurityContext, setting the required fields. + * + * @param ticketValidator the Ticket Validator. + * @param service the Service instance representing this uPortal instance. + * @param proxyRetriever the object used to retrieve proxies. + */ + public ThreadLocalAwareCasSecurityContext( + final TicketValidator ticketValidator, final Service service, + final ProxyRetriever proxyRetriever) { + super(ticketValidator, service, proxyRetriever); + } + + protected Service getService() { + return ServiceHolder.getService(); + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactory.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactory.java new file mode 100644 index 0000000..24aae2b --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.portal.security.ISecurityContext; + +/** + * Factory to instantiate ThreadLocalAwareCasSecurityContexts. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class ThreadLocalAwareCasSecurityContextFactory extends + AbstractCasSecurityContextFactory { + + public ISecurityContext getSecurityContext() { + return new ThreadLocalAwareCasSecurityContext(this.ticketValidator, + this.service, this.proxyRetriever); + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilter.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilter.java new file mode 100644 index 0000000..f20a934 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.web.filter.AbstractCasFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Filter to construct the service url from the request and place it in a + * ThreadLocal so its available to the + * {@link ThreadLocalAwareCasSecurityContext} in order to use it for Ticket + * validation. + *

This filter places the Service in a {@link ServiceHolder}. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class ThreadLocalAwareCasServiceFilter extends AbstractCasFilter { + + protected void doFilterInternal(final HttpServletRequest request, + final HttpServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + final boolean hasTicket = CommonUtils.isNotBlank(request + .getParameter("ticket")); + try { + if (hasTicket) { + final Service service = new SimpleService(constructServiceUrl( + request, response)); + ServiceHolder.setService(service); + } + + filterChain.doFilter(request, response); + } finally { + if (hasTicket) { + ServiceHolder.clearContext(); + } + } + } +} diff --git a/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServlet.java b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServlet.java new file mode 100644 index 0000000..3281922 --- /dev/null +++ b/cas-client-uportal/src/main/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServlet.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.uportal.org/license.html + */ +package org.jasig.cas.client.integration.uportal; + +import org.jasig.cas.client.proxy.AbstractProxyReceptorServlet; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.portal.spring.PortalApplicationContextFacade; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +/** + * Implementation of AbstractProxyReceptorServlet that retrieves the + * ProxyGrantingTicket storage from the Portal Application Context instead of a + * WebApplicationContext. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public final class UPortalConfiguredProxyReceptorServlet extends + AbstractProxyReceptorServlet { + + /** Unique Id for Serialization. */ + private static final long serialVersionUID = 6596608588362834646L; + + public void init(final ServletConfig servletConfig) throws ServletException { + logger.info("Retrieving ProxyGrantingTicketStorage from PortalApplicationContextFacade."); + setProxyGrantingTicketStorage((ProxyGrantingTicketStorage) PortalApplicationContextFacade + .getPortalApplicationContext() + .getBean( + AbstractCasSecurityContextFactory.CONST_CAS_PROXY_GRANTING_TICKET_STORAGE)); + } +} diff --git a/cas-client-uportal/src/main/resources/uportalCasConfigurationContext.xml b/cas-client-uportal/src/main/resources/uportalCasConfigurationContext.xml new file mode 100644 index 0000000..98a413b --- /dev/null +++ b/cas-client-uportal/src/main/resources/uportalCasConfigurationContext.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cas-client-uportal/src/test/clover/clover.license b/cas-client-uportal/src/test/clover/clover.license new file mode 100644 index 0000000..8506a9d --- /dev/null +++ b/cas-client-uportal/src/test/clover/clover.license @@ -0,0 +1,166 @@ +Product: Clover +License: Open Source License, 0.x, 1.x +Issued: Sun Feb 13 2005 22:45:43 CST +Expiry: Never +Key: aae4c7035b1208e316fa6d684 +Name: Scott Battaglia +Org: JA-SIG Central Authentication Service +Certificate: AAACCG+Ow8B7/zEbxOMqqKwwrdpP+a1COmJGHco7sCNLjHkHnajPF+dQW +Ct12PMy0uml0s9xuus5wKngJ9OFk5PFeh01dzQF66bhXH1bvegLfvja3Kle6BYtDv4LZgE +gk3E0aJN4IbgTn+TgUckSevXDR4KzK77NWJfrVzkxV3/JepYRA9UAbsXHiki9WjMIJfzoZ +unjvtTFd/vTOcyirgfT/dTPrG9PAGAjH+e37E9Xf7HnRSrmxtrGX+wdaBOlZFUIIcVHKT2 +IaGToLZnx7FvfE3rzyQCYtS+r0E+H61c+dANzySy5PEH2JEyL8M9JrwgYJSju1FWhxbXO2 +Gb7y3Diufo80+HWz6xrGzl9IlXRseoXHki8rllk5taXqVv5G3UIsTFzbjjWUDlykn557C2 +D4o9T0xQ/6dVFxak75o0MxP4aXGFMZNg/pCBH9DAU7/CKVKRBPAVx1PJ8vIy41MF4p9Mi1 +qmELNvjn9K9fwpaeiUG9qT8B0pfq/tTAObG2sZf7B1mFbA3YwEqjhNqLdkoca5swrS0DI1 +9OejIVIqjK+bvUZaqUDMxVX7fM6hwRvI9Wd+rwFG+X3wHYNPsZ+Cos8/BNPzIIoOh4SbTr +8vIqWUdPXM4HO26uAAVRKz6FknmwM/GQ7FyJBWgIXgXK51SLn+NcifxO8uywGewHzP00ki +frTPmy0+GrikleWry6BdWkg76hjrjQBalNlSmasi9yp9J8qdzVYvQlOBjS5EKWsvsSpXGY +MIdupkiv4a25aXsgdpGBy4GzcSUDioChq287HBBmYRIMVvp5OYbV3/+ERNhTlCQqb6Ck4g +891l1OJOEoMiDqcbDL8DNftlH4gybEE7zJXQRmmJKyw== +License Agreement: CLOVER VERSION 1 (ONE) SOFTWARE LICENSE AGREEMENT + +1. Licenses and Software + +Cortex eBusiness Pty Ltd, an Australian Proprietary Limited Company +("CENQUA") hereby grants to the purchaser (the "LICENSEE") a limited, +revocable, worldwide, non-exclusive, non-transferable, +non-sublicensable license to use the Clover version 1 (one) software +(the "Software"), including any minor upgrades thereof during the Term +(hereinafter defined) up to, but not including the next major version +of the Software. The licensee shall not, or knowingly allow others to, +reverse engineer, decompile, disassemble, modify, adapt, create +derivative works from or otherwise attempt to derive source code from +the Software provided. And, in accordance with the terms and +conditions of this Software License Agreement (the "Agreement"), the +Software shall be used solely by the licensed users in accordance with +the following edition specific conditions: + +a) Server Edition + +A Server Edition license entitles the Licensee to execute one instance +of Clover Server Edition on one (1) machine for the purposes of +instrumenting source code and generating reports. There are no +limitations on the use of the instrumented source code or generated +reports produced by Server Edition. + +b) Workstation Edition + +A Workstation Edition license entitles the licensee to use Clover +Workstation Edition on one (1) machine by one (1) individual end +user. Workstation Edition does not permit the generation of reports +for distribution. + +c) Team Edition + +A Team Edition license entitles the licensee to use Clover Team +edition on any number of machines solely by the licensed number of +users. Reports generated by Clover Team Edition are strictly for use +only by the licensed number of individual end users. + +2. License Fee + +In exchange for the License(s), the Licensee shall pay to Cenqua a +one-time, up front, non-refundable license fee. At the sole discretion +of Cenqua this fee will be waived for non-commercial +projects. Notwithstanding the Licensee's payment of the License Fee, +Cenqua reserves the right to terminate the License if Cenqua discovers +that the Licensee and/or the Licensee's use of the Software is in +breach of this Agreement. + +3. Proprietary Rights + +Cenqua will retain all right, title and interest in and to the +Software, all copies thereof, and Cenqua website(s), software, and +other intellectual property, including, but not limited to, ownership +of all copyrights, look and feel, trademark rights, design rights, +trade secret rights and any and all other intellectual property and +other proprietary rights therein. The Licensee will not directly or +indirectly obtain or attempt to obtain at any time, any right, title +or interest by registration or otherwise in or to the trademarks, +service marks, copyrights, trade names, symbols, logos or designations +or other intellectual property rights owned or used by Cenqua. All +technical manuals or other information provided by Cenqua to the +Licensee shall be the sole property of Cenqua. + +4. Term and Termination + +Subject to the other provisions hereof, this Agreement shall commence +upon the Licensee's opting into this Agreement and continue until the +Licensee discontinues use of the Software or the Agreement terminates +automatically upon the Licensee's breach of any term or condition of +this Agreement (the "Term"). Upon any such termination, the Licensee +will delete the Software immediately. + +5. Copying & Transfer + +The Licensee may copy the Software for back-up purposes only. The +Licensee may not assign or otherwise transfer the Software to any +third party. + +6. Specific Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE IS PROVIDED WITHOUT WARRANTY OF ANY KIND. CENQUA +DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. CENQUA WILL NOT BE LIABLE FOR ANY DAMAGES +ASSOCIATED WITH THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ORDINARY, +INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING +BUT NOT LIMITED TO DAMAGES RELATING TO LOST DATA OR LOST PROFITS, EVEN +IF CENQUA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Warranties and Representations + +Licensee Indemnification. CENQUA agrees to indemnify, defend and hold +the Licensee harmless from and against any and all liabilities, +damages, losses, claims, costs, and expenses (including reasonable +legal fees) arising out of or resulting from the Software or the use +thereof infringing upon, misappropriating or violating any patents, +copyrights, trademarks, or trade secret rights or other proprietary +rights of persons, firms or entities who are not parties to this +Agreement. + +CENQUA Indemnification. The Licensee warrants and represents that the +Licensee's actions with regard to the Software will be in compliance +with all applicable laws; and the Licensee agrees to indemnify, +defend, and hold CENQUA harmless from and against any and all +liabilities, damages, losses, claims, costs, and expenses (including +reasonable legal fees) arising out of or resulting from the +Licensee's failure to observe the use restrictions set forth herein. + +8. Publicity + +The Licensee grants permission for CENQUA to use Licensee's name +solely in customer lists. CENQUA shall not, without prior consent in +writing, use the Licensee's name, or that of its affiliates, in any +form with the specific exception of customer lists. CENQUA agrees to +remove Licensee's name from any and all materials within 7 days if +notified by the Licensee in writing. + +9. Governing Law + +This Agreement shall be governed by the laws of New South Wales, +Australia. + +10. Independent Contractors + +The parties are independent contractors with respect to each other, +and nothing in this Agreement shall be construed as creating an +employer-employee relationship, a partnership, agency relationship or +a joint venture between the parties. + +11. Assignment + +This Agreement is not assignable or transferable by the Licensee. +CENQUA in its sole discretion may transfer a license to a third party +at the written request of the Licensee. + +12. Entire Agreement + +This Agreement constitutes the entire agreement between the parties +concerning the Licensee's use of the Software. This Agreement +supersedes any prior verbal understanding between the parties and any +Licensee purchase order or other ordering document, regardless of +whether such document is received by CENQUA before or after execution +of this Agreement. This Agreement may be amended only in writing by +CENQUA. diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactoryTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactoryTests.java new file mode 100644 index 0000000..c397b64 --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextFactoryTests.java @@ -0,0 +1,24 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.jasig.portal.security.ISecurityContext; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class CasSecurityContextFactoryTests extends TestCase { + + private CasSecurityContextFactory casSecurityContextFactory; + + + protected void setUp() throws Exception { + this.casSecurityContextFactory = new CasSecurityContextFactory(); + } + + public void testGetter() { + final ISecurityContext casSecurityContext = this.casSecurityContextFactory.getSecurityContext(); + assertNotNull(casSecurityContext); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextTests.java new file mode 100644 index 0000000..5817c60 --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/CasSecurityContextTests.java @@ -0,0 +1,82 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimplePrincipal; +import org.jasig.cas.authentication.principal.SimpleService; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.AssertionImpl; +import org.jasig.cas.client.validation.TicketValidator; +import org.jasig.cas.client.validation.ValidationException; +import org.jasig.cas.client.proxy.ProxyRetriever; +import org.jasig.portal.security.PortalSecurityException; + +import java.util.HashMap; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class CasSecurityContextTests extends TestCase { + + private CasSecurityContext context; + + + protected void setUp() throws Exception { + this.context = new CasSecurityContext(new TicketValidator() { + + public Assertion validate(String ticketId, Service service) throws ValidationException { + return new AssertionImpl(new SimplePrincipal("test"), new HashMap(), null); + } + }, new SimpleService("test"), null); + this.context.getOpaqueCredentialsInstance().setCredentials("ticket"); + } + + public void testAuthenticate() throws Exception { + this.context.authenticate(); + + assertEquals("test", this.context.getPrincipal().getUID()); + assertTrue(this.context.isAuthenticated()); + assertNull(this.context.getProxyTicket(new SimpleService("test"))); + } + + public void testAuthenticateWithProxy() throws Exception { + this.context = new CasSecurityContext(new TicketValidator() { + + public Assertion validate(String ticketId, Service service) throws ValidationException { + return new AssertionImpl(new SimplePrincipal("test"), new HashMap(), "test"); + } + }, new SimpleService("test"), new ProxyRetriever() { + + public String getProxyTicketIdFor(String proxyGrantingTicketId, Service targetService) { + return "test"; + } + }); + this.context.getOpaqueCredentialsInstance().setCredentials("ticket"); + this.context.authenticate(); + assertEquals("test", this.context.getProxyTicket(new SimpleService("test"))); + + } + + public void testAuthenticateFail() { + this.context = new CasSecurityContext(new TicketValidator() { + + public Assertion validate(String ticketId, Service service) throws ValidationException { + throw new ValidationException("test"); + } + }, new SimpleService("test"), null); + this.context.getOpaqueCredentialsInstance().setCredentials("ticket"); + + try { + this.context.authenticate(); + fail("Exception expected."); + } catch (PortalSecurityException e) { + assertTrue(e.getCause() instanceof ValidationException); + } + } + + public void testGetAuthType() { + assertEquals(ICasSecurityContext.CAS_AUTHTYPE, this.context.getAuthType()); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ServiceHolderTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ServiceHolderTests.java new file mode 100644 index 0000000..a51511a --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ServiceHolderTests.java @@ -0,0 +1,32 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimpleService; + +/** + * @author Scott Battaglia + * @version $Revision$ $Datet$ + * @since 3.0 + * + */ +public class ServiceHolderTests extends TestCase { + + public void testSetGetService() { + final Service service = new SimpleService("test"); + ServiceHolder.setService(service); + + assertEquals(service, ServiceHolder.getService()); + } + + public void testClearContext() { + final Service service = new SimpleService("test"); + ServiceHolder.setService(service); + + assertEquals(service, ServiceHolder.getService()); + + ServiceHolder.clearContext(); + + assertNull(ServiceHolder.getService()); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactoryTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactoryTests.java new file mode 100644 index 0000000..24857ee --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextFactoryTests.java @@ -0,0 +1,25 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.jasig.portal.security.ISecurityContext; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class ThreadLocalAwareCasSecurityContextFactoryTests extends TestCase { + + private ThreadLocalAwareCasSecurityContextFactory factory; + + protected void setUp() throws Exception { + this.factory = new ThreadLocalAwareCasSecurityContextFactory(); + } + + public void testGetter() { + ISecurityContext context = this.factory.getSecurityContext(); + + assertNotNull(context); + assertTrue(context instanceof ThreadLocalAwareCasSecurityContext); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextTests.java new file mode 100644 index 0000000..30125a2 --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasSecurityContextTests.java @@ -0,0 +1,28 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.jasig.cas.authentication.principal.Service; +import org.jasig.cas.authentication.principal.SimpleService; + +/** + * @author Scott + */ +public class ThreadLocalAwareCasSecurityContextTests extends TestCase { + + private ThreadLocalAwareCasSecurityContext context; + + + protected void setUp() throws Exception { + final ThreadLocalAwareCasSecurityContextFactory factory = new ThreadLocalAwareCasSecurityContextFactory(); + this.context = (ThreadLocalAwareCasSecurityContext) factory.getSecurityContext(); + } + + public void testGetService() { + final Service service = new SimpleService("test"); + ServiceHolder.setService(service); + + assertEquals(service, this.context.getService()); + + ServiceHolder.clearContext(); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilterTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilterTests.java new file mode 100644 index 0000000..7983ff0 --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/ThreadLocalAwareCasServiceFilterTests.java @@ -0,0 +1,53 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.jasig.cas.authentication.principal.SimpleService; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class ThreadLocalAwareCasServiceFilterTests extends TestCase { + + private ThreadLocalAwareCasServiceFilter filter; + + + protected void setUp() throws Exception { + this.filter = new ThreadLocalAwareCasServiceFilter(); + this.filter.setServiceUrl("http://localhost"); + this.filter.init(); + } + + public void testServiceSetter() throws IOException, ServletException { + final MockHttpServletRequest request = new MockHttpServletRequest(); + + request.setParameter("ticket", "test"); + this.filter.doFilter(request, new MockHttpServletResponse(), new FilterChain() { + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException { + assertNotNull(ServiceHolder.getService()); + assertEquals(new SimpleService("http://localhost"), ServiceHolder.getService()); + } + }); + } + + public void testNoServiceSetter() throws IOException, ServletException { + final MockHttpServletRequest request = new MockHttpServletRequest(); + + this.filter.doFilter(request, new MockHttpServletResponse(), new FilterChain() { + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException { + assertNull(ServiceHolder.getService()); + } + }); + } +} diff --git a/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServletTests.java b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServletTests.java new file mode 100644 index 0000000..35acb2a --- /dev/null +++ b/cas-client-uportal/src/test/java/org/jasig/cas/client/integration/uportal/UPortalConfiguredProxyReceptorServletTests.java @@ -0,0 +1,18 @@ +package org.jasig.cas.client.integration.uportal; + +import junit.framework.TestCase; +import org.springframework.mock.web.MockServletContext; +import org.springframework.mock.web.MockServletConfig; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.0 + */ +public class UPortalConfiguredProxyReceptorServletTests extends TestCase { + + public void testInitialization() throws Exception { + final UPortalConfiguredProxyReceptorServlet servlet = new UPortalConfiguredProxyReceptorServlet(); + servlet.init(new MockServletConfig(new MockServletContext())); + } +} diff --git a/cas-client-uportal/src/test/resources/properties/beanRefFactory.xml b/cas-client-uportal/src/test/resources/properties/beanRefFactory.xml new file mode 100644 index 0000000..a2e0090 --- /dev/null +++ b/cas-client-uportal/src/test/resources/properties/beanRefFactory.xml @@ -0,0 +1,12 @@ + + + + + + + properties/uPortalConfigContext.xml + + + + \ No newline at end of file diff --git a/cas-client-uportal/src/test/resources/properties/uPortalConfigContext.xml b/cas-client-uportal/src/test/resources/properties/uPortalConfigContext.xml new file mode 100644 index 0000000..6fab676 --- /dev/null +++ b/cas-client-uportal/src/test/resources/properties/uPortalConfigContext.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cas-client.iml b/cas-client.iml new file mode 100644 index 0000000..fd6d193 --- /dev/null +++ b/cas-client.iml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cas-client.ipr b/cas-client.ipr new file mode 100644 index 0000000..bbe3802 --- /dev/null +++ b/cas-client.ipr @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cas-client.iws b/cas-client.iws new file mode 100644 index 0000000..be7ef31 --- /dev/null +++ b/cas-client.iws @@ -0,0 +1,737 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ad158ca --- /dev/null +++ b/pom.xml @@ -0,0 +1,144 @@ + + 4.0.0 + org.jasig.cas + 3.0-SNAPSHOT + cas-client + pom + JA-SIG CAS Client for Java + 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. + + http://www.ja-sig.org/products/cas/ + + JIRA + http://www.ja-sig.org/issues + + + QuickBuild + http://developer.ja-sig.org/builds/ + + 2006 + + + CAS Community Discussion List + http://tp.its.yale.edu/mailman/listinfo/cas + http://tp.its.yale.edu/mailman/listinfo/cas + cas@tp.its.yale.edu + http://tp.its.yale.edu/pipermail/cas/ + + http://news.gmane.org/gmane.comp.java.jasig.cas.user + + + + CAS Developers Discussion List + http://tp.its.yale.edu/mailman/listinfo/cas-dev + http://tp.its.yale.edu/mailman/listinfo/cas-dev + cas-dev@tp.its.yale.edu + http://tp.its.yale.edu/pipermail/cas-dev/ + + http://news.gmane.org/gmane.comp.java.jasig.cas.devel + + + + + + battags + Scott Battaglia + scott_battaglia@rutgers.edu + http://www.scottbattaglia.com + Rutgers, the State University of New Jersey + http://www.rutgers.edu + + Project Admin + Developer + + -5 + + + + + JA-SIG License for Use + http://www.ja-sig.org/products/cas/overview/license/ + manual + + + + scm:cvs:pserver:anonymous:@developer.ja-sig.org:/home/cvs/jasig:cas-client + scm:cvs:pserver:${username}@developer.ja-sig.org:/home/cvs/jasig:cas-client + + http://developer.ja-sig.org/source/browse/jasig/cas-clients/java-client + + + JA-SIG + http://www.ja-sig.org + + + + + junit + junit + 3.8.1 + test + + + commons-logging + commons-logging + 1.1 + compile + + + cas + cas-server + 3.0.5 + compile + + + org.springframework + spring-beans + 2.0-rc2 + compile + + + org.springframework + spring-core + 2.0-rc2 + compile + + + javax.servlet + servlet-api + 2.4 + compile + + + + + + jasig + JA-SIG Maven Repository + http://developer.ja-sig.org/maven/ + legacy + + + ibiblio + Ibiblio Repository + http://www.ibiblio.org/maven2 + + + + + mojo + Codehaus Repository + http://snapshots.maven.codehaus.org/maven2 + + + ibiblio + Ibiblio Repository + http://www.ibiblio.org/maven2 + + + + cas-client-core + cas-client-uportal + + \ No newline at end of file