diff --git a/.classpath b/.classpath
index 13063b1cba..e96de12118 100644
--- a/.classpath
+++ b/.classpath
@@ -21,5 +21,8 @@
AuthenticationManager
.
+ *
+ *
+ * Do not use this class directly. Instead configure CAS to use the {@link + * CasPasswordHandlerProxy}. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public final class CasPasswordHandler implements InitializingBean { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(CasPasswordHandler.class); + + //~ Instance fields ======================================================== + + private AuthenticationManager authenticationManager; + + //~ Methods ================================================================ + + public void setAuthenticationManager( + AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public AuthenticationManager getAuthenticationManager() { + return authenticationManager; + } + + public void afterPropertiesSet() throws Exception { + if (this.authenticationManager == null) { + throw new IllegalArgumentException( + "An AuthenticationManager is required"); + } + } + + /** + * Called byCasPasswordHandlerProxy
for individual
+ * authentication requests.
+ *
+ *
+ * Delegates to the configured AuthenticationManager
.
+ *
+ * This class works along with {@link CasPasswordHandler} to enable users to + * easily migrate from stand-alone Acegi Security System deployments to + * enterprise-wide CAS deployments. + *
+ * + *
+ * It should be noted that the Acegi Security System will operate as a CAS
+ * client irrespective of the PasswordHandler
used on the CAS
+ * server. In other words, this class need not be used on the CAS
+ * server if not desired. It exists solely for the convenience of users
+ * wishing have CAS delegate to an Acegi Security System-based
+ * AuthenticationManager
.
+ *
+ * This class works requires a properly configured
+ * CasPasswordHandler
. On the first authentication request, the
+ * class will use Spring's {@link
+ * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
+ * method to obtain an ApplicationContext
instance, inside which
+ * must be a configured CasPasswordHandler
instance. The
+ * CasPasswordHandlerProxy
will then delegate authentication
+ * requests to that instance.
+ *
+ * To configure CAS to use this class, edit CAS' web.xml
and
+ * define the edu.yale.its.tp.cas.authHandler
context parameter
+ * with the value
+ * net.sf.acegisecurity.adapters.cas.CasPasswordHandlerProxy
.
+ *
+ * Delegates to the CasPasswordHandler
.
+ *
CasPasswordHandler
or the
+ * ServletRequest
was not of type
+ * HttpServletRequest
+ */
+ public boolean authenticate(ServletRequest request, String username,
+ String password) {
+ if (ctx == null) {
+ if (!(request instanceof HttpServletRequest)) {
+ throw new IllegalArgumentException(
+ "Can only process HttpServletRequest");
+ }
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+ ctx = this.getContext(httpRequest);
+ }
+
+ if (handler == null) {
+ Map beans = ctx.getBeansOfType(CasPasswordHandler.class, true, true);
+
+ if (beans.size() == 0) {
+ throw new IllegalArgumentException(
+ "Bean context must contain at least one bean of type CasPasswordHandler");
+ }
+
+ String beanName = (String) beans.keySet().iterator().next();
+ handler = (CasPasswordHandler) beans.get(beanName);
+ }
+
+ return handler.authenticate(request, username, password);
+ }
+
+ /**
+ * Allows test cases to override where application context obtained from.
+ *
+ * @param httpRequest which can be used to find the
+ * ServletContext
+ *
+ * @return the Spring application context
+ */
+ protected ApplicationContext getContext(HttpServletRequest httpRequest) {
+ return WebApplicationContextUtils.getRequiredWebApplicationContext(httpRequest.getSession()
+ .getServletContext());
+ }
+}
diff --git a/adapters/cas/src/main/java/org/acegisecurity/adapters/cas/package.html b/adapters/cas/src/main/java/org/acegisecurity/adapters/cas/package.html
new file mode 100644
index 0000000000..85dafa4bb6
--- /dev/null
+++ b/adapters/cas/src/main/java/org/acegisecurity/adapters/cas/package.html
@@ -0,0 +1,7 @@
+
+
+Adapter to Yale Central Authentication Service (CAS).
+
+
+
+
diff --git a/adapters/cas/src/main/resources/org/acegisecurity/adapters/cas/applicationContext.xml b/adapters/cas/src/main/resources/org/acegisecurity/adapters/cas/applicationContext.xml
new file mode 100644
index 0000000000..78cf5216dc
--- /dev/null
+++ b/adapters/cas/src/main/resources/org/acegisecurity/adapters/cas/applicationContext.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+ * This AuthenticationProvider
is capable of validating {@link
+ * UsernamePasswordAuthenticationToken} requests which contain a
+ * principal
name equal to either {@link
+ * CasProcessingFilter#CAS_STATEFUL_IDENTIFIER} or {@link
+ * CasProcessingFilter#CAS_STATELESS_IDENTIFIER}. It can also validate a
+ * previously created {@link CasAuthenticationToken}.
+ *
Authentication
.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
+ //~ Instance fields ========================================================
+
+ private List proxyList;
+ private Object credentials;
+ private Object principal;
+ private String proxyGrantingTicketIou;
+ private GrantedAuthority[] authorities;
+ private int keyHash;
+
+ //~ Constructors ===========================================================
+
+ /**
+ * Constructor.
+ *
+ * @param key to identify if this object made by a given {@link
+ * CasAuthenticationProvider}
+ * @param principal the username from CAS (cannot be null
)
+ * @param credentials the service/proxy ticket ID from CAS (cannot be
+ * null
)
+ * @param authorities the authorities granted to the user (from {@link
+ * CasAuthoritiesPopulator}) (cannot be null
)
+ * @param proxyList the list of proxies from CAS (cannot be
+ * null
)
+ * @param proxyGrantingTicketIou the PGT-IOU ID from CAS (cannot be
+ * null
)
+ *
+ * @throws IllegalArgumentException if a null
was passed
+ */
+ public CasAuthenticationToken(String key, Object principal,
+ Object credentials, GrantedAuthority[] authorities, List proxyList,
+ String proxyGrantingTicketIou) {
+ if ((key == null) || ("".equals(key)) || (principal == null)
+ || "".equals(principal) || (credentials == null)
+ || "".equals(credentials) || (authorities == null)
+ || (proxyList == null) || (proxyGrantingTicketIou == null)
+ || ("".equals(proxyGrantingTicketIou))) {
+ throw new IllegalArgumentException(
+ "Cannot pass null or empty values to constructor");
+ }
+
+ for (int i = 0; i < authorities.length; i++) {
+ if (authorities[i] == null) {
+ throw new IllegalArgumentException("Granted authority element "
+ + i
+ + " is null - GrantedAuthority[] cannot contain any null elements");
+ }
+ }
+
+ this.keyHash = key.hashCode();
+ this.principal = principal;
+ this.credentials = credentials;
+ this.authorities = authorities;
+ this.proxyList = proxyList;
+ this.proxyGrantingTicketIou = proxyGrantingTicketIou;
+ }
+
+ protected CasAuthenticationToken() {
+ throw new IllegalArgumentException("Cannot use default constructor");
+ }
+
+ //~ Methods ================================================================
+
+ /**
+ * Ignored (always true
).
+ *
+ * @param isAuthenticated ignored
+ */
+ public void setAuthenticated(boolean isAuthenticated) {
+ // ignored
+ }
+
+ /**
+ * Always returns true
.
+ *
+ * @return true
+ */
+ public boolean isAuthenticated() {
+ return true;
+ }
+
+ public GrantedAuthority[] getAuthorities() {
+ return this.authorities;
+ }
+
+ public Object getCredentials() {
+ return this.credentials;
+ }
+
+ public int getKeyHash() {
+ return this.keyHash;
+ }
+
+ public Object getPrincipal() {
+ return this.principal;
+ }
+
+ public String getProxyGrantingTicketIou() {
+ return proxyGrantingTicketIou;
+ }
+
+ public List getProxyList() {
+ return proxyList;
+ }
+
+ public boolean equals(Object obj) {
+ if (!super.equals(obj)) {
+ return false;
+ }
+
+ if (obj instanceof CasAuthenticationToken) {
+ CasAuthenticationToken test = (CasAuthenticationToken) obj;
+
+ // proxyGrantingTicketIou is never null due to constructor
+ if (!this.getProxyGrantingTicketIou().equals(test
+ .getProxyGrantingTicketIou())) {
+ return false;
+ }
+
+ // proxyList is never null due to constructor
+ if (!this.getProxyList().equals(test.getProxyList())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ System.out.println("THey're not equal");
+
+ return false;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(super.toString());
+ sb.append("; Credentials (Service/Proxy Ticket): " + this.credentials);
+ sb.append("; Proxy-Granting Ticket IOU: " + this.proxyGrantingTicketIou);
+ sb.append("; Proxy List: " + this.proxyList.toString());
+
+ return sb.toString();
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/cas/CasAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/cas/CasAuthoritiesPopulator.java
new file mode 100644
index 0000000000..e5b247c488
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/cas/CasAuthoritiesPopulator.java
@@ -0,0 +1,59 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.GrantedAuthority;
+
+
+/**
+ * Populates the GrantedAuthority[]
objects for a CAS
+ * authenticated user.
+ *
+ *
+ * CAS does not provide the authorities (roles) granted to a user. It merely
+ * authenticates their identity. As the Acegi Security System for Spring needs
+ * to know the authorities granted to a user in order to construct a valid
+ * Authentication
object, implementations of this interface will
+ * provide this information.
+ *
+ * Implementations should not perform any caching. They will only be called + * when a refresh is required. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public interface CasAuthoritiesPopulator { + //~ Methods ================================================================ + + /** + * Obtains the granted authorities for the specified user. + * + *
+ * May throw any AuthenticationException
or return
+ * null
if the authorities are unavailable.
+ *
+ * CAS 1.0 allowed services to receive a service ticket and then validate it. + * CAS 2.0 allows services to receive a service ticket and then validate it + * with a proxy callback URL. The callback will enable the CAS server to + * authenticate the service. In doing so the service will receive a + * proxy-granting ticket and a proxy-granting ticket IOU. The IOU is just an + * internal record that a proxy-granting ticket is due to be received via the + * callback URL. + *
+ * + *+ * With a proxy-granting ticket, a service can request the CAS server provides + * it with a proxy ticket. A proxy ticket is just a service ticket, but the + * CAS server internally tracks the list (chain) of services used to build the + * proxy ticket. The proxy ticket is then presented to the target service. + *
+ * + *
+ * If this application is a target service of a proxy ticket, the
+ * CasProxyDecider
resolves whether or not the proxy list is
+ * trusted. Applications should only trust services they allow to impersonate
+ * an end user.
+ *
+ * If this application is a service that should never accept proxy-granting + * tickets, the implementation should reject tickets that present a proxy list + * with any members. If the list has no members, it indicates the CAS server + * directly authenticated the user (ie there are no services which proxied the + * user authentication). + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public interface CasProxyDecider { + //~ Methods ================================================================ + + /** + * Decides whether the proxy list is trusted. + * + *
+ * Must throw any ProxyUntrustedException
if the proxy list is
+ * untrusted.
+ *
ProxyUntrustedException
with the specified
+ * message.
+ *
+ * @param msg the detail message.
+ */
+ public ProxyUntrustedException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs a ProxyUntrustedException
with the specified
+ * message and root cause.
+ *
+ * @param msg the detail message.
+ * @param t root cause
+ */
+ public ProxyUntrustedException(String msg, Throwable t) {
+ super(msg, t);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/cas/StatelessTicketCache.java b/core/src/main/java/org/acegisecurity/providers/cas/StatelessTicketCache.java
new file mode 100644
index 0000000000..ad406e040e
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/cas/StatelessTicketCache.java
@@ -0,0 +1,116 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+/**
+ * Caches CAS service tickets and CAS proxy tickets for stateless connections.
+ *
+ *
+ * When a service ticket or proxy ticket is validated against the CAS server,
+ * it is unable to be used again. Most types of callers are stateful and are
+ * associated with a given HttpSession
. This allows the
+ * affirmative CAS validation outcome to be stored in the
+ * HttpSession
, meaning the removal of the ticket from the CAS
+ * server is not an issue issue.
+ *
+ * Stateless callers, such as remoting protocols, cannot take advantage of
+ * HttpSession
. If the stateless caller is located a significant
+ * network distance from the CAS server, acquiring a fresh service ticket or
+ * proxy ticket for each invocation would be expensive.
+ *
+ * To avoid this issue with stateless callers, it is expected stateless callers
+ * will obtain a single service ticket or proxy ticket, and then present this
+ * same ticket to the Acegi Security System secured application on each
+ * occasion. As no HttpSession
is available for such callers, the
+ * affirmative CAS validation outcome cannot be stored in this location.
+ *
+ * The StatelessTicketCache
enables the service tickets and proxy
+ * tickets belonging to stateless callers to be placed in a cache. This
+ * in-memory cache stores the CasAuthenticationToken
, effectively
+ * providing the same capability as a HttpSession
with the ticket
+ * identifier being the key rather than a session identifier.
+ *
+ * Implementations should provide a reasonable timeout on stored entries, such + * that the stateless caller are not required to unnecessarily acquire fresh + * CAS service tickets or proxy tickets. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public interface StatelessTicketCache { + //~ Methods ================================================================ + + /** + * Retrieves theCasAuthenticationToken
associated with the
+ * specified ticket.
+ *
+ *
+ * If not found, returns a
+ * null
CasAuthenticationToken
.
+ *
CasAuthenticationToken
to the cache.
+ *
+ * + * The {@link CasAuthenticationToken#getCredentials()} method is used to + * retrieve the service ticket number. + *
+ * + * @param token to be added to the cache + */ + public void putTicketInCache(CasAuthenticationToken token); + + /** + * Removes the specified ticket from the cache, as per {@link + * #removeTicketFromCache(String)}. + * + *+ * Implementations should use {@link + * CasAuthenticationToken#getCredentials()} to obtain the ticket and then + * delegate to to the {@link #removeTicketFromCache(String)} method. + *
+ * + * @param token to be removed + */ + public void removeTicketFromCache(CasAuthenticationToken token); + + /** + * Removes the specified ticket from the cache, meaning that future calls + * will require a new service ticket. + * + *+ * This is in case applications wish to provide a session termination + * capability for their stateless clients. + *
+ * + * @param serviceTicket to be removed + */ + public void removeTicketFromCache(String serviceTicket); +} diff --git a/core/src/main/java/org/acegisecurity/providers/cas/TicketResponse.java b/core/src/main/java/org/acegisecurity/providers/cas/TicketResponse.java new file mode 100644 index 0000000000..d7600c744c --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/TicketResponse.java @@ -0,0 +1,102 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.cas; + +import java.util.List; +import java.util.Vector; + + +/** + * Represents a CAS service ticket in native CAS form. + * + * @author Ben Alex + * @version $Id$ + */ +public class TicketResponse { + //~ Instance fields ======================================================== + + private List proxyList; + private String proxyGrantingTicketIou; + private String user; + + //~ Constructors =========================================================== + + /** + * Constructor. + * + *
+ * If null
is passed into the proxyList
or
+ * proxyGrantingTicketIou
, suitable defaults are established.
+ * However, null
cannot be passed for the user
+ * argument.
+ *
null
or
+ * an empty String
)
+ * @param proxyList as provided by CAS (may be null
)
+ * @param proxyGrantingTicketIou as provided by CAS (may be
+ * null
)
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public TicketResponse(String user, List proxyList,
+ String proxyGrantingTicketIou) {
+ if (proxyList == null) {
+ proxyList = new Vector();
+ }
+
+ if (proxyGrantingTicketIou == null) {
+ proxyGrantingTicketIou = "";
+ }
+
+ if ((user == null) || "".equals(user)) {
+ throw new IllegalArgumentException(
+ "Cannot pass null or empty String for User");
+ }
+
+ this.user = user;
+ this.proxyList = proxyList;
+ this.proxyGrantingTicketIou = proxyGrantingTicketIou;
+ }
+
+ protected TicketResponse() {
+ throw new IllegalArgumentException("Cannot use default constructor");
+ }
+
+ //~ Methods ================================================================
+
+ public String getProxyGrantingTicketIou() {
+ return proxyGrantingTicketIou;
+ }
+
+ public List getProxyList() {
+ return proxyList;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(super.toString());
+ sb.append(": User: " + this.user);
+ sb.append("; Proxy-Granting Ticket IOU: " + this.proxyGrantingTicketIou);
+ sb.append("; Proxy List: " + this.proxyList.toString());
+
+ return sb.toString();
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/cas/TicketValidator.java b/core/src/main/java/org/acegisecurity/providers/cas/TicketValidator.java
new file mode 100644
index 0000000000..c9be4d38cb
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/cas/TicketValidator.java
@@ -0,0 +1,53 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+import net.sf.acegisecurity.AuthenticationException;
+
+
+/**
+ * Validates a CAS service ticket.
+ *
+ *
+ * Implementations must accept CAS proxy tickets, in addition to CAS service
+ * tickets. If proxy tickets should be rejected, this is resolved by a {@link
+ * CasProxyDecider} implementation (not by the TicketValidator
).
+ *
+ * Implementations may request a proxy granting ticket if wish, although this + * behaviour is not mandatory. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public interface TicketValidator { + //~ Methods ================================================================ + + /** + * Returns information about the ticket, if it is valid for this service. + * + *
+ * Must throw an AuthenticationException
if the ticket is not
+ * valid for this service.
+ *
+ * Defaults to 20 minutes. + *
+ * + * @return Returns the minutes an element remains in the cache + */ + public int getMinutesToIdle() { + return minutesToIdle; + } + + public void afterPropertiesSet() throws Exception { + manager = CacheManager.create(); + + // Cache name, max memory, overflowToDisk, eternal, timeToLive, timeToIdle + cache = new Cache("ehCacheBasedTicketCache", Integer.MAX_VALUE, false, + false, minutesToIdle * 60, minutesToIdle * 60); + manager.addCache(cache); + } + + public void putTicketInCache(CasAuthenticationToken token) { + Element element = new Element(token.getCredentials().toString(), token); + System.out.println("Adding " + element.getKey()); + cache.put(element); + } + + public void removeTicketFromCache(CasAuthenticationToken token) { + this.removeTicketFromCache(token.getCredentials().toString()); + } + + public void removeTicketFromCache(String serviceTicket) { + cache.remove(serviceTicket); + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/cas/package.html b/core/src/main/java/org/acegisecurity/providers/cas/package.html new file mode 100644 index 0000000000..698c9baf40 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/package.html @@ -0,0 +1,6 @@ + + +An authentication provider that can process Yale Central Authentication Service (CAS) +service tickets and proxy tickets. + + diff --git a/core/src/main/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulator.java new file mode 100644 index 0000000000..2b29cc446f --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulator.java @@ -0,0 +1,67 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.cas.populator; + +import net.sf.acegisecurity.AuthenticationException; +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.providers.cas.CasAuthoritiesPopulator; +import net.sf.acegisecurity.providers.dao.AuthenticationDao; + +import org.springframework.beans.factory.InitializingBean; + + +/** + * Populates the CAS authorities via an {@link AuthenticationDao}. + * + *
+ * The additional information (username, password, enabled status etc) an
+ * AuthenticationDao
implementation provides about a
+ * User
is ignored. Only the GrantedAuthority
s are
+ * relevant to this class.
+ *
+ * Also accepts the request if there was no proxy (ie the user directly + * authenticated against this service). + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class AcceptAnyCasProxy implements CasProxyDecider { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(AcceptAnyCasProxy.class); + + //~ Methods ================================================================ + + public void confirmProxyListTrusted(List proxyList) + throws ProxyUntrustedException { + if (proxyList == null) { + throw new IllegalArgumentException("proxyList cannot be null"); + } + + if (logger.isDebugEnabled()) { + logger.debug("Always accepting proxy list: " + proxyList.toString()); + } + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDecider.java b/core/src/main/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDecider.java new file mode 100644 index 0000000000..5a0e60b58f --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDecider.java @@ -0,0 +1,87 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.cas.proxy; + +import net.sf.acegisecurity.providers.cas.CasProxyDecider; +import net.sf.acegisecurity.providers.cas.ProxyUntrustedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.InitializingBean; + +import java.util.List; + + +/** + * Accepts proxied requests if the closest proxy is named in the + *validProxies
list.
+ *
+ * + * Also accepts the request if there was no proxy (ie the user directly + * authenticated against this service). + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class NamedCasProxyDecider implements CasProxyDecider, InitializingBean { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(NamedCasProxyDecider.class); + + //~ Instance fields ======================================================== + + private List validProxies; + + //~ Methods ================================================================ + + public void setValidProxies(List validProxies) { + this.validProxies = validProxies; + } + + public List getValidProxies() { + return validProxies; + } + + public void afterPropertiesSet() throws Exception { + if (this.validProxies == null) { + throw new IllegalArgumentException( + "A validProxies list must be set"); + } + } + + public void confirmProxyListTrusted(List proxyList) + throws ProxyUntrustedException { + if (proxyList == null) { + throw new IllegalArgumentException("proxyList cannot be null"); + } + + if (logger.isDebugEnabled()) { + logger.debug("Proxy list: " + proxyList.toString()); + } + + if (proxyList.size() == 0) { + // A Service Ticket (not a Proxy Ticket) + return; + } + + if (!validProxies.contains(proxyList.get(0))) { + throw new ProxyUntrustedException("Nearest proxy '" + + proxyList.get(0) + "' is untrusted"); + } + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/cas/proxy/RejectProxyTickets.java b/core/src/main/java/org/acegisecurity/providers/cas/proxy/RejectProxyTickets.java new file mode 100644 index 0000000000..92aab08fc7 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/proxy/RejectProxyTickets.java @@ -0,0 +1,63 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.cas.proxy; + +import net.sf.acegisecurity.providers.cas.CasProxyDecider; +import net.sf.acegisecurity.providers.cas.ProxyUntrustedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.List; + + +/** + * Accepts no proxied requests. + * + *+ * This class should be used if only service tickets wish to be accepted (ie no + * proxy tickets at all). + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class RejectProxyTickets implements CasProxyDecider { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(RejectProxyTickets.class); + + //~ Methods ================================================================ + + public void confirmProxyListTrusted(List proxyList) + throws ProxyUntrustedException { + if (proxyList == null) { + throw new IllegalArgumentException("proxyList cannot be null"); + } + + if (proxyList.size() == 0) { + // A Service Ticket (not a Proxy Ticket) + return; + } + + if (logger.isDebugEnabled()) { + logger.debug("Proxies are unacceptable; proxy list provided: " + + proxyList.toString()); + } + + throw new ProxyUntrustedException("Proxy tickets are rejected"); + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/cas/proxy/package.html b/core/src/main/java/org/acegisecurity/providers/cas/proxy/package.html new file mode 100644 index 0000000000..cc163a2f6f --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/proxy/package.html @@ -0,0 +1,6 @@ + + +Implementations that decide whether proxy lists of +CAS authentications are trusted. + + diff --git a/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidator.java b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidator.java new file mode 100644 index 0000000000..1cd25cc9c3 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidator.java @@ -0,0 +1,96 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.cas.ticketvalidator; + +import net.sf.acegisecurity.providers.cas.TicketValidator; +import net.sf.acegisecurity.ui.cas.ServiceProperties; + +import org.springframework.beans.factory.InitializingBean; + + +/** + * Convenience abstract base forTicketValidator
s.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AbstractTicketValidator implements TicketValidator,
+ InitializingBean {
+ //~ Instance fields ========================================================
+
+ private ServiceProperties serviceProperties;
+ private String casValidate;
+ private String trustStore;
+
+ //~ Methods ================================================================
+
+ public void setCasValidate(String casValidate) {
+ this.casValidate = casValidate;
+ }
+
+ /**
+ * Mandatory URL to CAS' proxy ticket valiation service.
+ *
+ *
+ * This is usually something like
+ * https://www.mycompany.com/cas/proxyValidate
.
+ *
javax.net.ssl.trustStore
.
+ *
+ * @return the javax.net.ssl.trustStore
that will be set
+ * during bean initialization, or null
to leave the
+ * system property unchanged
+ */
+ public String getTrustStore() {
+ return trustStore;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if ((this.casValidate == null) || "".equals(casValidate)) {
+ throw new IllegalArgumentException("A casValidate URL must be set");
+ }
+
+ if (serviceProperties == null) {
+ throw new IllegalArgumentException(
+ "serviceProperties must be specified");
+ }
+
+ if ((trustStore != null) && (!"".equals(trustStore))) {
+ System.setProperty("javax.net.ssl.trustStore", trustStore);
+ }
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidator.java b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidator.java
new file mode 100644
index 0000000000..17a405bd58
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidator.java
@@ -0,0 +1,128 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.ticketvalidator;
+
+import edu.yale.its.tp.cas.client.ProxyTicketValidator;
+
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationServiceException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.providers.cas.TicketResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Uses CAS' ProxyTicketValidator
to validate a service ticket.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasProxyTicketValidator extends AbstractTicketValidator {
+ //~ Static fields/initializers =============================================
+
+ private static final Log logger = LogFactory.getLog(CasProxyTicketValidator.class);
+
+ //~ Instance fields ========================================================
+
+ private String proxyCallbackUrl;
+
+ //~ Methods ================================================================
+
+ public void setProxyCallbackUrl(String proxyCallbackUrl) {
+ this.proxyCallbackUrl = proxyCallbackUrl;
+ }
+
+ /**
+ * Optional callback URL to obtain a proxy-granting ticket from CAS.
+ *
+ *
+ * This callback URL belongs to the Acegi Security System for Spring
+ * secured application. We suggest you use CAS'
+ * ProxyTicketReceptor
servlet to receive this callback and
+ * manage the proxy-granting ticket list. The callback URL is usually
+ * something like
+ * https://www.mycompany.com/application/casProxy/receptor
.
+ *
+ * If left null
, the CasAuthenticationToken
will
+ * not have a proxy granting ticket IOU and there will be no
+ * proxy-granting ticket callback. Accordingly, the Acegi Securty System
+ * for Spring secured application will be unable to obtain a proxy ticket
+ * to call another CAS-secured service on behalf of the user. This is not
+ * really an issue for most applications.
+ *
null
if not used
+ */
+ public String getProxyCallbackUrl() {
+ return proxyCallbackUrl;
+ }
+
+ public TicketResponse confirmTicketValid(String serviceTicket)
+ throws AuthenticationException {
+ // Attempt to validate presented ticket using CAS' ProxyTicketValidator class
+ ProxyTicketValidator pv = new ProxyTicketValidator();
+
+ pv.setCasValidateUrl(super.getCasValidate());
+ pv.setServiceTicket(serviceTicket);
+ pv.setService(super.getServiceProperties().getService());
+
+ if (super.getServiceProperties().isSendRenew()) {
+ logger.warn(
+ "The current CAS ProxyTicketValidator does not support the 'renew' property. The ticket cannot be validated as having been issued by a 'renew' authentication. It is expected this will be corrected in a future version of CAS' ProxyTicketValidator.");
+ }
+
+ if ((this.proxyCallbackUrl != null)
+ && (!"".equals(this.proxyCallbackUrl))) {
+ pv.setProxyCallbackUrl(proxyCallbackUrl);
+ }
+
+ return validateNow(pv);
+ }
+
+ /**
+ * Perform the actual remote invocation. Protected to enable replacement
+ * during tests.
+ *
+ * @param pv the populated ProxyTicketValidator
+ *
+ * @return the TicketResponse
+ *
+ * @throws AuthenticationServiceException
+ * ifProxyTicketValidator
internally fails
+ * @throws BadCredentialsException DOCUMENT ME!
+ */
+ protected TicketResponse validateNow(ProxyTicketValidator pv)
+ throws AuthenticationServiceException, BadCredentialsException {
+ try {
+ pv.validate();
+ } catch (Exception internalProxyTicketValidatorProblem) {
+ throw new AuthenticationServiceException(internalProxyTicketValidatorProblem
+ .getMessage());
+ }
+
+ if (!pv.isAuthenticationSuccesful()) {
+ throw new BadCredentialsException(pv.getErrorCode() + ": "
+ + pv.getErrorMessage());
+ }
+
+ return new TicketResponse(pv.getUser(), pv.getProxyList(),
+ pv.getPgtIou());
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/package.html b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/package.html
new file mode 100644
index 0000000000..26bceb87c2
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/cas/ticketvalidator/package.html
@@ -0,0 +1,5 @@
+
+
+Implementations that validate service tickets.
+
+
diff --git a/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilter.java
new file mode 100644
index 0000000000..c4dea63ece
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilter.java
@@ -0,0 +1,111 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import net.sf.acegisecurity.ui.AbstractProcessingFilter;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Processes a CAS service ticket.
+ *
+ *
+ * A service ticket consists of an opaque ticket string. It arrives at this
+ * filter by the user's browser successfully authenticating using CAS, and
+ * then receiving a HTTP redirect to a service
. The opal ticket
+ * string is presented in the ticket
request parameter. This
+ * filter monitors the service
URL so it can receive the service
+ * ticket and process it. The CAS server knows which service
URL
+ * to use via the {@link ServiceProperties#getService()} method.
+ *
+ * Processing the service ticket involves creating a
+ * UsernamePasswordAuthenticationToken
which uses {@link
+ * #CAS_STATEFUL_IDENTIFIER} for the principal
and the opaque
+ * ticket string as the credentials
.
+ *
+ * The configured AuthenticationManager
is expected to provide a
+ * provider that can recognise
+ * UsernamePasswordAuthenticationToken
s containing this special
+ * principal
name, and process them accordingly by validation
+ * with the CAS server.
+ *
+ * Do not use this class directly. Instead configure
+ * web.xml
to use the {@link
+ * net.sf.acegisecurity.util.FilterToBeanProxy}.
+ *
HttpSession
will result in a new authentication attempt on
+ * every request.
+ */
+ public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
+
+ //~ Methods ================================================================
+
+ /**
+ * This filter by default responds to
+ * /j_acegi_cas_security_check
.
+ *
+ * @return the default
+ */
+ public String getDefaultFilterProcessesUrl() {
+ return "/j_acegi_cas_security_check";
+ }
+
+ public Authentication attemptAuthentication(HttpServletRequest request)
+ throws AuthenticationException {
+ String username = CAS_STATEFUL_IDENTIFIER;
+ String password = request.getParameter("ticket");
+
+ if (password == null) {
+ password = "";
+ }
+
+ UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
+ password);
+
+ return this.getAuthenticationManager().authenticate(authRequest);
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {}
+}
diff --git a/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPoint.java b/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPoint.java
new file mode 100644
index 0000000000..dfbcc4b91b
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPoint.java
@@ -0,0 +1,102 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import net.sf.acegisecurity.intercept.web.AuthenticationEntryPoint;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Used by the SecurityEnforcementFilter
to commence
+ * authentication via the Yale Central Authentication Service (CAS).
+ *
+ *
+ * The user's browser will be redirected to the Yale CAS enterprise-wide login
+ * page. This page is specified by the loginUrl
property. Once
+ * login is complete, the CAS login page will redirect to the page indicated
+ * by the service
property. The service
is a HTTP
+ * URL belonging to the current application. The service
URL is
+ * monitored by the {@link CasProcessingFilter}, which will validate the CAS
+ * login was successful.
+ *
https://www.mycompany.com/cas/login
.
+ *
+ * @return the enterprise-wide CAS login URL
+ */
+ public String getLoginUrl() {
+ return loginUrl;
+ }
+
+ public void setServiceProperties(ServiceProperties serviceProperties) {
+ this.serviceProperties = serviceProperties;
+ }
+
+ public ServiceProperties getServiceProperties() {
+ return serviceProperties;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if ((loginUrl == null) || "".equals(loginUrl)) {
+ throw new IllegalArgumentException("loginUrl must be specified");
+ }
+
+ if (serviceProperties == null) {
+ throw new IllegalArgumentException(
+ "serviceProperties must be specified");
+ }
+ }
+
+ public void commence(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+ String url;
+
+ if (serviceProperties.isSendRenew()) {
+ url = loginUrl + "?renew=true" + "&service="
+ + serviceProperties.getService();
+ } else {
+ url = loginUrl + "?service=" + serviceProperties.getService();
+ }
+
+ ((HttpServletResponse) response).sendRedirect(url);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/ui/cas/ServiceProperties.java b/core/src/main/java/org/acegisecurity/ui/cas/ServiceProperties.java
new file mode 100644
index 0000000000..d87258a8af
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/ui/cas/ServiceProperties.java
@@ -0,0 +1,86 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * Stores properties related to this CAS service.
+ *
+ * + * Each web application capable of processing CAS tickets is known as a + * service. This class stores the properties that are relevant to the local + * CAS service, being the application that is being secured by the Acegi + * Security System for Spring. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class ServiceProperties implements InitializingBean { + //~ Instance fields ======================================================== + + private String service; + + private boolean sendRenew = false; + + //~ Methods ================================================================ + + public void setSendRenew(boolean sendRenew) { + this.sendRenew = sendRenew; + } + + /** + * Indicates whether therenew
parameter should be sent to the
+ * CAS login URL and CAS validation URL.
+ * If true
, it will
+ * force CAS to authenticate the user again (even if the user has
+ * previously authenticated). During ticket validation it will require the
+ * ticket was generated as a consequence of an explicit login. High
+ * security applications would probably set this to true
.
+ * Defaults to false
, providing automated single sign on.
+ *
+ * @return whether to send the renew
parameter to CAS
+ */
+ public boolean isSendRenew() {
+ return sendRenew;
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ /**
+ * Represents the service the user is authenticating to.
+ *
+ * This service is the callback URL
+ * belonging to the local Acegi Security System for Spring secured
+ * application. For example,
+ * https://www.mycompany.com/application/j_acegi_cas_security_check
+ *
+ * @return the URL of the service the user is authenticating to
+ */
+ public String getService() {
+ return service;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if ((service == null) || "".equals(service)) {
+ throw new IllegalArgumentException("service must be specified");
+ }
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/ui/cas/package.html b/core/src/main/java/org/acegisecurity/ui/cas/package.html
new file mode 100644
index 0000000000..d327cda63c
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/ui/cas/package.html
@@ -0,0 +1,6 @@
+
+
+Authenticates standard web browser users via
+Yale Central Authentication Service (CAS).
+
+
diff --git a/core/src/main/java/org/acegisecurity/userdetails/User.java b/core/src/main/java/org/acegisecurity/userdetails/User.java
index 45c3e532eb..e5c5813c26 100644
--- a/core/src/main/java/org/acegisecurity/userdetails/User.java
+++ b/core/src/main/java/org/acegisecurity/userdetails/User.java
@@ -15,6 +15,8 @@
package net.sf.acegisecurity.providers.dao;
+import java.io.Serializable;
+
import net.sf.acegisecurity.GrantedAuthority;
@@ -24,7 +26,7 @@ import net.sf.acegisecurity.GrantedAuthority;
* @author Ben Alex
* @version $Id$
*/
-public class User {
+public class User implements Serializable {
//~ Instance fields ========================================================
private String password;
@@ -53,9 +55,10 @@ public class User {
*/
public User(String username, String password, boolean enabled,
GrantedAuthority[] authorities) throws IllegalArgumentException {
- if ((username == null) || (password == null) || (authorities == null)) {
+ if (((username == null) || "".equals(username)) || (password == null)
+ || "".equals(password) || (authorities == null)) {
throw new IllegalArgumentException(
- "Cannot pass null values to constructor");
+ "Cannot pass null or empty values to constructor");
}
for (int i = 0; i < authorities.length; i++) {
diff --git a/core/src/test/java/org/acegisecurity/MockHttpServletRequest.java b/core/src/test/java/org/acegisecurity/MockHttpServletRequest.java
index 6691240418..2a89e02b95 100644
--- a/core/src/test/java/org/acegisecurity/MockHttpServletRequest.java
+++ b/core/src/test/java/org/acegisecurity/MockHttpServletRequest.java
@@ -47,7 +47,7 @@ import javax.servlet.http.HttpSession;
public class MockHttpServletRequest implements HttpServletRequest {
//~ Instance fields ========================================================
- private HttpSession session;
+ private HttpSession session = new MockHttpSession();
private Map headersMap = new HashMap();
private Map paramMap = new HashMap();
private Principal principal;
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationProviderTests.java
new file mode 100644
index 0000000000..139a8bd1e9
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationProviderTests.java
@@ -0,0 +1,404 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import net.sf.acegisecurity.providers.cas.ticketvalidator.AbstractTicketValidator;
+import net.sf.acegisecurity.ui.cas.CasProcessingFilter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link CasAuthenticationProvider}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasAuthenticationProviderTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public CasAuthenticationProviderTests() {
+ super();
+ }
+
+ public CasAuthenticationProviderTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(CasAuthenticationProviderTests.class);
+ }
+
+ public void testAuthenticateStateful() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider(true));
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(CasProcessingFilter.CAS_STATEFUL_IDENTIFIER,
+ "ST-123");
+
+ Authentication result = cap.authenticate(token);
+
+ // Confirm ST-123 was NOT added to the cache
+ assertTrue(cache.getByTicketId("ST-456") == null);
+
+ if (!(result instanceof CasAuthenticationToken)) {
+ fail("Should have returned a CasAuthenticationToken");
+ }
+
+ CasAuthenticationToken casResult = (CasAuthenticationToken) result;
+ assertEquals("marissa", casResult.getPrincipal());
+ assertEquals("PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt",
+ casResult.getProxyGrantingTicketIou());
+ assertEquals("https://localhost/portal/j_acegi_cas_security_check",
+ casResult.getProxyList().get(0));
+ assertEquals("ST-123", casResult.getCredentials());
+ assertEquals(new GrantedAuthorityImpl("ROLE_A"),
+ casResult.getAuthorities()[0]);
+ assertEquals(new GrantedAuthorityImpl("ROLE_B"),
+ casResult.getAuthorities()[1]);
+ assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
+
+ // Now confirm the CasAuthenticationToken is automatically re-accepted.
+ // To ensure TicketValidator not called again, set it to deliver an exception...
+ cap.setTicketValidator(new MockTicketValidator(false));
+
+ Authentication laterResult = cap.authenticate(result);
+ assertEquals(result, laterResult);
+ }
+
+ public void testAuthenticateStateless() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider(true));
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(CasProcessingFilter.CAS_STATELESS_IDENTIFIER,
+ "ST-456");
+
+ Authentication result = cap.authenticate(token);
+
+ // Confirm ST-456 was added to the cache
+ assertTrue(cache.getByTicketId("ST-456") != null);
+
+ if (!(result instanceof CasAuthenticationToken)) {
+ fail("Should have returned a CasAuthenticationToken");
+ }
+
+ assertEquals("marissa", result.getPrincipal());
+ assertEquals("ST-456", result.getCredentials());
+
+ // Now try to authenticate again. To ensure TicketValidator not
+ // called again, set it to deliver an exception...
+ cap.setTicketValidator(new MockTicketValidator(false));
+
+ // Previously created UsernamePasswordAuthenticationToken is OK
+ Authentication newResult = cap.authenticate(token);
+ assertEquals("marissa", newResult.getPrincipal());
+ assertEquals("ST-456", newResult.getCredentials());
+ }
+
+ public void testDetectsAMissingTicketId() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider(true));
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(CasProcessingFilter.CAS_STATEFUL_IDENTIFIER,
+ "");
+
+ try {
+ Authentication result = cap.authenticate(token);
+ fail("Should have thrown BadCredentialsException");
+ } catch (BadCredentialsException expected) {
+ assertEquals("Failed to provide a CAS service ticket to validate",
+ expected.getMessage());
+ }
+ }
+
+ public void testDetectsAnInvalidKey() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider(true));
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY",
+ "test", "credentials",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("XX")},
+ new Vector(), "IOU-xxx");
+
+ try {
+ Authentication result = cap.authenticate(token);
+ fail("Should have thrown BadCredentialsException");
+ } catch (BadCredentialsException expected) {
+ assertEquals("The presented CasAuthenticationToken does not contain the expected key",
+ expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingAuthoritiesPopulator()
+ throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+
+ try {
+ cap.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A casAuthoritiesPopulator must be set",
+ expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingKey() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+
+ try {
+ cap.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated",
+ expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingProxyDecider() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+
+ try {
+ cap.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A casProxyDecider must be set", expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingStatelessTicketCache()
+ throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setKey("qwerty");
+ cap.setTicketValidator(new MockTicketValidator(true));
+
+ try {
+ cap.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A statelessTicketCache must be set",
+ expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingTicketValidator() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider(true));
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+
+ try {
+ cap.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A ticketValidator must be set", expected.getMessage());
+ }
+ }
+
+ public void testGettersSetters() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ assertTrue(cap.getCasAuthoritiesPopulator() != null);
+ assertTrue(cap.getCasProxyDecider() != null);
+ assertEquals("qwerty", cap.getKey());
+ assertTrue(cap.getStatelessTicketCache() != null);
+ assertTrue(cap.getTicketValidator() != null);
+ }
+
+ public void testIgnoresClassesItDoesNotSupport() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ TestingAuthenticationToken token = new TestingAuthenticationToken("user",
+ "password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
+ assertFalse(cap.supports(TestingAuthenticationToken.class));
+
+ // Try it anyway
+ assertEquals(null, cap.authenticate(token));
+ }
+
+ public void testIgnoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal()
+ throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
+ cap.setCasProxyDecider(new MockProxyDecider());
+ cap.setKey("qwerty");
+ cap.setStatelessTicketCache(new MockStatelessTicketCache());
+ cap.setTicketValidator(new MockTicketValidator(true));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user",
+ "password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
+ assertEquals(null, cap.authenticate(token));
+ }
+
+ public void testSupports() {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ assertTrue(cap.supports(UsernamePasswordAuthenticationToken.class));
+ assertTrue(cap.supports(CasAuthenticationToken.class));
+ }
+
+ //~ Inner Classes ==========================================================
+
+ private class MockAuthoritiesPopulator implements CasAuthoritiesPopulator {
+ public GrantedAuthority[] getAuthorities(String casUserId)
+ throws AuthenticationException {
+ return new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A"), new GrantedAuthorityImpl(
+ "ROLE_B")};
+ }
+ }
+
+ private class MockProxyDecider implements CasProxyDecider {
+ private boolean acceptProxy;
+
+ public MockProxyDecider(boolean acceptProxy) {
+ this.acceptProxy = acceptProxy;
+ }
+
+ private MockProxyDecider() {
+ super();
+ }
+
+ public void confirmProxyListTrusted(List proxyList)
+ throws ProxyUntrustedException {
+ if (acceptProxy) {
+ return;
+ } else {
+ throw new ProxyUntrustedException("As requested from mock");
+ }
+ }
+ }
+
+ private class MockStatelessTicketCache implements StatelessTicketCache {
+ private Map cache = new HashMap();
+
+ public CasAuthenticationToken getByTicketId(String serviceTicket) {
+ return (CasAuthenticationToken) cache.get(serviceTicket);
+ }
+
+ public void putTicketInCache(CasAuthenticationToken token) {
+ cache.put(token.getCredentials().toString(), token);
+ }
+
+ public void removeTicketFromCache(CasAuthenticationToken token) {
+ throw new UnsupportedOperationException(
+ "mock method not implemented");
+ }
+
+ public void removeTicketFromCache(String serviceTicket) {
+ throw new UnsupportedOperationException(
+ "mock method not implemented");
+ }
+ }
+
+ private class MockTicketValidator extends AbstractTicketValidator {
+ private boolean returnTicket;
+
+ public MockTicketValidator(boolean returnTicket) {
+ this.returnTicket = returnTicket;
+ }
+
+ private MockTicketValidator() {
+ super();
+ }
+
+ public TicketResponse confirmTicketValid(String serviceTicket)
+ throws AuthenticationException {
+ if (returnTicket) {
+ List list = new Vector();
+ list.add("https://localhost/portal/j_acegi_cas_security_check");
+
+ return new TicketResponse("marissa", list,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ }
+
+ throw new BadCredentialsException("As requested from mock");
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java b/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java
new file mode 100644
index 0000000000..61f954e81d
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java
@@ -0,0 +1,283 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link CasAuthenticationToken}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasAuthenticationTokenTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public CasAuthenticationTokenTests() {
+ super();
+ }
+
+ public CasAuthenticationTokenTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(CasAuthenticationTokenTests.class);
+ }
+
+ public void testConstructorRejectsNulls() {
+ try {
+ new CasAuthenticationToken(null, "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", null, "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", "Test", null,
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", "Test", "Password", null,
+ new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, null,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(), null);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+
+ try {
+ new CasAuthenticationToken("key", "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), null, new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testEqualsWhenEqual() {
+ List proxyList1 = new Vector();
+ proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token1 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList1,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ List proxyList2 = new Vector();
+ proxyList2.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token2 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList2,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ assertEquals(token1, token2);
+ }
+
+ public void testGetters() {
+ // Build the proxy list returned in the ticket from CAS
+ List proxyList = new Vector();
+ proxyList.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ assertEquals("key".hashCode(), token.getKeyHash());
+ assertEquals("Test", token.getPrincipal());
+ assertEquals("Password", token.getCredentials());
+ assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority());
+ assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
+ assertEquals("PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt",
+ token.getProxyGrantingTicketIou());
+ assertEquals(proxyList, token.getProxyList());
+ }
+
+ public void testNoArgConstructor() {
+ try {
+ new CasAuthenticationToken();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testNotEqualsDueToAbstractParentEqualsCheck() {
+ List proxyList1 = new Vector();
+ proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token1 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList1,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ List proxyList2 = new Vector();
+ proxyList2.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token2 = new CasAuthenticationToken("key",
+ "OTHER_VALUE", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList2,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ assertTrue(!token1.equals(token2));
+ }
+
+ public void testNotEqualsDueToDifferentAuthenticationClass() {
+ List proxyList1 = new Vector();
+ proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token1 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList1,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test",
+ "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")});
+ token2.setAuthenticated(true);
+
+ assertTrue(!token1.equals(token2));
+ }
+
+ public void testNotEqualsDueToProxyGrantingTicket() {
+ List proxyList1 = new Vector();
+ proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token1 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList1,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ List proxyList2 = new Vector();
+ proxyList2.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token2 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList2, "PGTIOU-SOME_OTHER_VALUE");
+
+ assertTrue(!token1.equals(token2));
+ }
+
+ public void testNotEqualsDueToProxyList() {
+ List proxyList1 = new Vector();
+ proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token1 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList1,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ List proxyList2 = new Vector();
+ proxyList2.add(
+ "https://localhost/SOME_OTHER_PORTAL/j_acegi_cas_security_check");
+
+ CasAuthenticationToken token2 = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList2,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+
+ assertTrue(!token1.equals(token2));
+ }
+
+ public void testSetAuthenticatedIgnored() {
+ CasAuthenticationToken token = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ assertTrue(token.isAuthenticated());
+ token.setAuthenticated(false); // ignored
+ assertTrue(token.isAuthenticated());
+ }
+
+ public void testToString() {
+ CasAuthenticationToken token = new CasAuthenticationToken("key",
+ "Test", "Password",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ String result = token.toString();
+ assertTrue(result.lastIndexOf("Proxy List:") != -1);
+ assertTrue(result.lastIndexOf("Proxy-Granting Ticket IOU:") != -1);
+ assertTrue(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1);
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/TicketResponseTests.java b/core/src/test/java/org/acegisecurity/providers/cas/TicketResponseTests.java
new file mode 100644
index 0000000000..ed2d121900
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/TicketResponseTests.java
@@ -0,0 +1,102 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link TicketResponse}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class TicketResponseTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public TicketResponseTests() {
+ super();
+ }
+
+ public TicketResponseTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(TicketResponseTests.class);
+ }
+
+ public void testConstructorAcceptsNullProxyGrantingTicketIOU() {
+ TicketResponse ticket = new TicketResponse("marissa", new Vector(), null);
+ assertEquals("", ticket.getProxyGrantingTicketIou());
+ }
+
+ public void testConstructorAcceptsNullProxyList() {
+ TicketResponse ticket = new TicketResponse("marissa", null,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ assertEquals(new Vector(), ticket.getProxyList());
+ }
+
+ public void testConstructorRejectsNullUser() {
+ try {
+ new TicketResponse(null, new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testGetters() {
+ // Build the proxy list returned in the ticket from CAS
+ List proxyList = new Vector();
+ proxyList.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ TicketResponse ticket = new TicketResponse("marissa", proxyList,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ assertEquals("marissa", ticket.getUser());
+ assertEquals(proxyList, ticket.getProxyList());
+ assertEquals("PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt",
+ ticket.getProxyGrantingTicketIou());
+ }
+
+ public void testNoArgConstructor() {
+ try {
+ new TicketResponse();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testToString() {
+ TicketResponse ticket = new TicketResponse("marissa", null,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ String result = ticket.toString();
+ assertTrue(result.lastIndexOf("Proxy List:") != -1);
+ assertTrue(result.lastIndexOf("Proxy-Granting Ticket IOU:") != -1);
+ assertTrue(result.lastIndexOf("User:") != -1);
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCacheTests.java b/core/src/test/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCacheTests.java
new file mode 100644
index 0000000000..d88efe42b3
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCacheTests.java
@@ -0,0 +1,89 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.cache;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.cas.CasAuthenticationToken;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link EhCacheBasedTicketCache}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class EhCacheBasedTicketCacheTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public EhCacheBasedTicketCacheTests() {
+ super();
+ }
+
+ public EhCacheBasedTicketCacheTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(EhCacheBasedTicketCacheTests.class);
+ }
+
+ public void testCacheOperation() throws Exception {
+ EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
+ cache.afterPropertiesSet();
+
+ // Check it gets stored in the cache
+ cache.putTicketInCache(getToken());
+ assertEquals(getToken(),
+ cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
+
+ // Check it gets removed from the cache
+ cache.removeTicketFromCache(getToken());
+ assertNull(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
+
+ // Check it doesn't return values for null or unknown service tickets
+ assertNull(cache.getByTicketId(null));
+ assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
+ }
+
+ public void testGettersSetters() {
+ EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
+ cache.setMinutesToIdle(5);
+ assertEquals(5, cache.getMinutesToIdle());
+ }
+
+ private CasAuthenticationToken getToken() {
+ List proxyList = new Vector();
+ proxyList.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ return new CasAuthenticationToken("key", "marissa",
+ "ST-0-ER94xMJmn6pha35CQRoZ",
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")}, proxyList,
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulatorTests.java b/core/src/test/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulatorTests.java
new file mode 100644
index 0000000000..71ea54160d
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/populator/DaoCasAuthoritiesPopulatorTests.java
@@ -0,0 +1,148 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.populator;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.dao.AuthenticationDao;
+import net.sf.acegisecurity.providers.dao.User;
+import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.DataRetrievalFailureException;
+
+
+/**
+ * Tests {@link DaoCasAuthoritiesPopulator}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class DaoCasAuthoritiesPopulatorTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public DaoCasAuthoritiesPopulatorTests() {
+ super();
+ }
+
+ public DaoCasAuthoritiesPopulatorTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(DaoCasAuthoritiesPopulatorTests.class);
+ }
+
+ public void testDetectsMissingAuthenticationDao() throws Exception {
+ DaoCasAuthoritiesPopulator populator = new DaoCasAuthoritiesPopulator();
+
+ try {
+ populator.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("An authenticationDao must be set",
+ expected.getMessage());
+ }
+ }
+
+ public void testGetGrantedAuthoritiesForInvalidUsername()
+ throws Exception {
+ DaoCasAuthoritiesPopulator populator = new DaoCasAuthoritiesPopulator();
+ populator.setAuthenticationDao(new MockAuthenticationDaoUserMarissa());
+ populator.afterPropertiesSet();
+
+ try {
+ populator.getAuthorities("scott");
+ fail("Should have thrown UsernameNotFoundException");
+ } catch (UsernameNotFoundException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testGetGrantedAuthoritiesForValidUsername()
+ throws Exception {
+ DaoCasAuthoritiesPopulator populator = new DaoCasAuthoritiesPopulator();
+ populator.setAuthenticationDao(new MockAuthenticationDaoUserMarissa());
+ populator.afterPropertiesSet();
+
+ GrantedAuthority[] results = populator.getAuthorities("marissa");
+ assertEquals(2, results.length);
+ assertEquals(new GrantedAuthorityImpl("ROLE_ONE"), results[0]);
+ assertEquals(new GrantedAuthorityImpl("ROLE_TWO"), results[1]);
+ }
+
+ public void testGetGrantedAuthoritiesWhenDaoThrowsException()
+ throws Exception {
+ DaoCasAuthoritiesPopulator populator = new DaoCasAuthoritiesPopulator();
+ populator.setAuthenticationDao(new MockAuthenticationDaoSimulateBackendError());
+ populator.afterPropertiesSet();
+
+ try {
+ populator.getAuthorities("THE_DAO_WILL_FAIL");
+ fail("Should have thrown DataRetrievalFailureException");
+ } catch (DataRetrievalFailureException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testGettersSetters() {
+ DaoCasAuthoritiesPopulator populator = new DaoCasAuthoritiesPopulator();
+ AuthenticationDao dao = new MockAuthenticationDaoUserMarissa();
+ populator.setAuthenticationDao(dao);
+ assertEquals(dao, populator.getAuthenticationDao());
+ }
+
+ //~ Inner Classes ==========================================================
+
+ private class MockAuthenticationDaoSimulateBackendError
+ implements AuthenticationDao {
+ public long getRefreshDuration() {
+ return 0;
+ }
+
+ public User loadUserByUsername(String username)
+ throws UsernameNotFoundException, DataAccessException {
+ throw new DataRetrievalFailureException(
+ "This mock simulator is designed to fail");
+ }
+ }
+
+ private class MockAuthenticationDaoUserMarissa implements AuthenticationDao {
+ public long getRefreshDuration() {
+ return 0;
+ }
+
+ public User loadUserByUsername(String username)
+ throws UsernameNotFoundException, DataAccessException {
+ if ("marissa".equals(username)) {
+ return new User("marissa", "koala", true,
+ new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+ "ROLE_TWO")});
+ } else {
+ throw new UsernameNotFoundException("Could not find: "
+ + username);
+ }
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/proxy/AcceptAnyCasProxyTests.java b/core/src/test/java/org/acegisecurity/providers/cas/proxy/AcceptAnyCasProxyTests.java
new file mode 100644
index 0000000000..57667badc7
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/proxy/AcceptAnyCasProxyTests.java
@@ -0,0 +1,66 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.proxy;
+
+import junit.framework.TestCase;
+
+import java.util.Vector;
+
+
+/**
+ * Tests {@link AcceptAnyCasProxy}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AcceptAnyCasProxyTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public AcceptAnyCasProxyTests() {
+ super();
+ }
+
+ public AcceptAnyCasProxyTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(AcceptAnyCasProxyTests.class);
+ }
+
+ public void testDoesNotAcceptNull() {
+ AcceptAnyCasProxy proxyDecider = new AcceptAnyCasProxy();
+
+ try {
+ proxyDecider.confirmProxyListTrusted(null);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("proxyList cannot be null", expected.getMessage());
+ }
+ }
+
+ public void testNormalOperation() {
+ AcceptAnyCasProxy proxyDecider = new AcceptAnyCasProxy();
+ proxyDecider.confirmProxyListTrusted(new Vector());
+ assertTrue(true); // as no Exception thrown
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDeciderTests.java b/core/src/test/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDeciderTests.java
new file mode 100644
index 0000000000..f4074f3e2d
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/proxy/NamedCasProxyDeciderTests.java
@@ -0,0 +1,141 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.proxy;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.providers.cas.ProxyUntrustedException;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link NamedCasProxyDecider}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class NamedCasProxyDeciderTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public NamedCasProxyDeciderTests() {
+ super();
+ }
+
+ public NamedCasProxyDeciderTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(NamedCasProxyDeciderTests.class);
+ }
+
+ public void testAcceptsIfNearestProxyIsAuthorized()
+ throws Exception {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+
+ // Build the ticket returned from CAS
+ List proxyList = new Vector();
+ proxyList.add("https://localhost/newPortal/j_acegi_cas_security_check");
+
+ // Build the list of valid nearest proxies
+ List validProxies = new Vector();
+ validProxies.add("https://localhost/portal/j_acegi_cas_security_check");
+ validProxies.add(
+ "https://localhost/newPortal/j_acegi_cas_security_check");
+ proxyDecider.setValidProxies(validProxies);
+ proxyDecider.afterPropertiesSet();
+
+ proxyDecider.confirmProxyListTrusted(proxyList);
+ assertTrue(true);
+ }
+
+ public void testAcceptsIfNoProxiesInTicket() {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+ List proxyList = new Vector(); // no proxies in list
+
+ proxyDecider.confirmProxyListTrusted(proxyList);
+ assertTrue(true);
+ }
+
+ public void testDetectsMissingValidProxiesList() throws Exception {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+
+ try {
+ proxyDecider.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A validProxies list must be set",
+ expected.getMessage());
+ }
+ }
+
+ public void testDoesNotAcceptNull() {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+
+ try {
+ proxyDecider.confirmProxyListTrusted(null);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("proxyList cannot be null", expected.getMessage());
+ }
+ }
+
+ public void testGettersSetters() {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+
+ // Build the list of valid nearest proxies
+ List validProxies = new Vector();
+ validProxies.add("https://localhost/portal/j_acegi_cas_security_check");
+ validProxies.add(
+ "https://localhost/newPortal/j_acegi_cas_security_check");
+ proxyDecider.setValidProxies(validProxies);
+
+ assertEquals(validProxies, proxyDecider.getValidProxies());
+ }
+
+ public void testRejectsIfNearestProxyIsNotAuthorized()
+ throws Exception {
+ NamedCasProxyDecider proxyDecider = new NamedCasProxyDecider();
+
+ // Build the ticket returned from CAS
+ List proxyList = new Vector();
+ proxyList.add(
+ "https://localhost/untrustedWebApp/j_acegi_cas_security_check");
+
+ // Build the list of valid nearest proxies
+ List validProxies = new Vector();
+ validProxies.add("https://localhost/portal/j_acegi_cas_security_check");
+ validProxies.add(
+ "https://localhost/newPortal/j_acegi_cas_security_check");
+ proxyDecider.setValidProxies(validProxies);
+ proxyDecider.afterPropertiesSet();
+
+ try {
+ proxyDecider.confirmProxyListTrusted(proxyList);
+ fail("Should have thrown ProxyUntrustedException");
+ } catch (ProxyUntrustedException expected) {
+ assertTrue(true);
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/proxy/RejectProxyTicketsTests.java b/core/src/test/java/org/acegisecurity/providers/cas/proxy/RejectProxyTicketsTests.java
new file mode 100644
index 0000000000..0fed184111
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/proxy/RejectProxyTicketsTests.java
@@ -0,0 +1,84 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.proxy;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.providers.cas.ProxyUntrustedException;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link RejectProxyTickets}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RejectProxyTicketsTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public RejectProxyTicketsTests() {
+ super();
+ }
+
+ public RejectProxyTicketsTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(RejectProxyTicketsTests.class);
+ }
+
+ public void testAcceptsIfNoProxiesInTicket() {
+ RejectProxyTickets proxyDecider = new RejectProxyTickets();
+ List proxyList = new Vector(); // no proxies in list
+
+ proxyDecider.confirmProxyListTrusted(proxyList);
+ assertTrue(true);
+ }
+
+ public void testDoesNotAcceptNull() {
+ RejectProxyTickets proxyDecider = new RejectProxyTickets();
+
+ try {
+ proxyDecider.confirmProxyListTrusted(null);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("proxyList cannot be null", expected.getMessage());
+ }
+ }
+
+ public void testRejectsIfAnyProxyInList() {
+ RejectProxyTickets proxyDecider = new RejectProxyTickets();
+ List proxyList = new Vector();
+ proxyList.add("https://localhost/webApp/j_acegi_cas_security_check");
+
+ try {
+ proxyDecider.confirmProxyListTrusted(proxyList);
+ fail("Should have thrown ProxyUntrustedException");
+ } catch (ProxyUntrustedException expected) {
+ assertTrue(true);
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidatorTests.java b/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidatorTests.java
new file mode 100644
index 0000000000..d24e2ffecc
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/AbstractTicketValidatorTests.java
@@ -0,0 +1,143 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.ticketvalidator;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.providers.cas.TicketResponse;
+import net.sf.acegisecurity.ui.cas.ServiceProperties;
+
+import java.util.Vector;
+
+
+/**
+ * Tests {@link AbstractTicketValidator}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AbstractTicketValidatorTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public AbstractTicketValidatorTests() {
+ super();
+ }
+
+ public AbstractTicketValidatorTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(AbstractTicketValidatorTests.class);
+ }
+
+ public void testDetectsMissingCasValidate() throws Exception {
+ AbstractTicketValidator tv = new MockAbstractTicketValidator();
+ tv.setServiceProperties(new ServiceProperties());
+
+ try {
+ tv.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("A casValidate URL must be set", expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingServiceProperties() throws Exception {
+ AbstractTicketValidator tv = new MockAbstractTicketValidator();
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+
+ try {
+ tv.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("serviceProperties must be specified",
+ expected.getMessage());
+ }
+ }
+
+ public void testGetters() throws Exception {
+ AbstractTicketValidator tv = new MockAbstractTicketValidator();
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+ assertEquals("https://company.com/cas/proxyvalidate",
+ tv.getCasValidate());
+
+ tv.setServiceProperties(new ServiceProperties());
+ assertTrue(tv.getServiceProperties() != null);
+
+ tv.afterPropertiesSet();
+
+ tv.setTrustStore("/some/file/cacerts");
+ assertEquals("/some/file/cacerts", tv.getTrustStore());
+ }
+
+ public void testSystemPropertySetDuringAfterPropertiesSet()
+ throws Exception {
+ AbstractTicketValidator tv = new MockAbstractTicketValidator();
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+ assertEquals("https://company.com/cas/proxyvalidate",
+ tv.getCasValidate());
+
+ tv.setServiceProperties(new ServiceProperties());
+ assertTrue(tv.getServiceProperties() != null);
+
+ tv.setTrustStore("/some/file/cacerts");
+ assertEquals("/some/file/cacerts", tv.getTrustStore());
+
+ String before = System.getProperty("javax.net.ssl.trustStore");
+ tv.afterPropertiesSet();
+ assertEquals("/some/file/cacerts",
+ System.getProperty("javax.net.ssl.trustStore"));
+
+ if (before == null) {
+ System.setProperty("javax.net.ssl.trustStore", "");
+ } else {
+ System.setProperty("javax.net.ssl.trustStore", before);
+ }
+ }
+
+ //~ Inner Classes ==========================================================
+
+ private class MockAbstractTicketValidator extends AbstractTicketValidator {
+ private boolean returnTicket;
+
+ public MockAbstractTicketValidator(boolean returnTicket) {
+ this.returnTicket = returnTicket;
+ }
+
+ private MockAbstractTicketValidator() {
+ super();
+ }
+
+ public TicketResponse confirmTicketValid(String serviceTicket)
+ throws AuthenticationException {
+ if (returnTicket) {
+ return new TicketResponse("user", new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ }
+
+ throw new BadCredentialsException("As requested by mock");
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidatorTests.java b/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidatorTests.java
new file mode 100644
index 0000000000..92170205a7
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/providers/cas/ticketvalidator/CasProxyTicketValidatorTests.java
@@ -0,0 +1,138 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.cas.ticketvalidator;
+
+import edu.yale.its.tp.cas.client.ProxyTicketValidator;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AuthenticationServiceException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.providers.cas.TicketResponse;
+import net.sf.acegisecurity.ui.cas.ServiceProperties;
+
+import java.util.Vector;
+
+
+/**
+ * Tests {@link CasProxyTicketValidator}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasProxyTicketValidatorTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public CasProxyTicketValidatorTests() {
+ super();
+ }
+
+ public CasProxyTicketValidatorTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(CasProxyTicketValidatorTests.class);
+ }
+
+ public void testGetters() {
+ CasProxyTicketValidator tv = new CasProxyTicketValidator();
+ tv.setProxyCallbackUrl("http://my.com/webapp/casProxy/someValidator");
+ assertEquals("http://my.com/webapp/casProxy/someValidator",
+ tv.getProxyCallbackUrl());
+ }
+
+ public void testNormalOperation() {
+ ServiceProperties sp = new ServiceProperties();
+ sp.setSendRenew(true);
+ sp.setService("https://my.com/webapp//j_acegi_cas_security_check");
+
+ CasProxyTicketValidator tv = new MockCasProxyTicketValidator(true, false);
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+ tv.setServiceProperties(sp);
+ tv.setProxyCallbackUrl("http://my.com/webapp/casProxy/someValidator");
+
+ TicketResponse response = tv.confirmTicketValid(
+ "ST-0-ER94xMJmn6pha35CQRoZ");
+
+ assertEquals("user", response.getUser());
+ }
+
+ public void testProxyTicketValidatorInternalExceptionsGracefullyHandled() {
+ CasProxyTicketValidator tv = new MockCasProxyTicketValidator(false, true);
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+ tv.setServiceProperties(new ServiceProperties());
+ tv.setProxyCallbackUrl("http://my.com/webapp/casProxy/someValidator");
+
+ try {
+ tv.confirmTicketValid("ST-0-ER94xMJmn6pha35CQRoZ");
+ fail("Should have thrown AuthenticationServiceException");
+ } catch (AuthenticationServiceException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testValidationFailsOkAndOperationWithoutAProxyCallbackUrl() {
+ CasProxyTicketValidator tv = new MockCasProxyTicketValidator(false,
+ false);
+ tv.setCasValidate("https://company.com/cas/proxyvalidate");
+ tv.setServiceProperties(new ServiceProperties());
+
+ try {
+ tv.confirmTicketValid("ST-0-ER94xMJmn6pha35CQRoZ");
+ fail("Should have thrown BadCredentialsExpected");
+ } catch (BadCredentialsException expected) {
+ assertTrue(true);
+ }
+ }
+
+ //~ Inner Classes ==========================================================
+
+ private class MockCasProxyTicketValidator extends CasProxyTicketValidator {
+ private boolean returnTicket;
+ private boolean throwAuthenticationServiceException;
+
+ public MockCasProxyTicketValidator(boolean returnTicket,
+ boolean throwAuthenticationServiceException) {
+ this.returnTicket = returnTicket;
+ this.throwAuthenticationServiceException = throwAuthenticationServiceException;
+ }
+
+ private MockCasProxyTicketValidator() {
+ super();
+ }
+
+ protected TicketResponse validateNow(ProxyTicketValidator pv)
+ throws AuthenticationServiceException, BadCredentialsException {
+ if (returnTicket) {
+ return new TicketResponse("user", new Vector(),
+ "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
+ }
+
+ if (throwAuthenticationServiceException) {
+ throw new AuthenticationServiceException("As requested by mock");
+ }
+
+ throw new BadCredentialsException("As requested by mock");
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java
new file mode 100644
index 0000000000..f3444b3a6c
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java
@@ -0,0 +1,126 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+
+
+/**
+ * Tests {@link CasProcessingFilterEntryPoint}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasProcessingFilterEntryPointTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public CasProcessingFilterEntryPointTests() {
+ super();
+ }
+
+ public CasProcessingFilterEntryPointTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(CasProcessingFilterEntryPointTests.class);
+ }
+
+ public void testDetectsMissingLoginFormUrl() throws Exception {
+ CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
+ ep.setServiceProperties(new ServiceProperties());
+
+ try {
+ ep.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("loginUrl must be specified", expected.getMessage());
+ }
+ }
+
+ public void testDetectsMissingServiceProperties() throws Exception {
+ CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
+ ep.setLoginUrl("https://cas/login");
+
+ try {
+ ep.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("serviceProperties must be specified",
+ expected.getMessage());
+ }
+ }
+
+ public void testGettersSetters() {
+ CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
+ ep.setLoginUrl("https://cas/login");
+ assertEquals("https://cas/login", ep.getLoginUrl());
+
+ ep.setServiceProperties(new ServiceProperties());
+ assertTrue(ep.getServiceProperties() != null);
+ }
+
+ public void testNormalOperationWithRenewFalse() throws Exception {
+ ServiceProperties sp = new ServiceProperties();
+ sp.setSendRenew(false);
+ sp.setService(
+ "https://mycompany.com/bigWebApp/j_acegi_cas_security_check");
+
+ CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
+ ep.setLoginUrl("https://cas/login");
+ ep.setServiceProperties(sp);
+
+ MockHttpServletRequest request = new MockHttpServletRequest(
+ "/some_path");
+
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ ep.afterPropertiesSet();
+ ep.commence(request, response);
+ assertEquals("https://cas/login?service=https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
+ response.getRedirect());
+ }
+
+ public void testNormalOperationWithRenewTrue() throws Exception {
+ ServiceProperties sp = new ServiceProperties();
+ sp.setSendRenew(true);
+ sp.setService(
+ "https://mycompany.com/bigWebApp/j_acegi_cas_security_check");
+
+ CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
+ ep.setLoginUrl("https://cas/login");
+ ep.setServiceProperties(sp);
+
+ MockHttpServletRequest request = new MockHttpServletRequest(
+ "/some_path");
+
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ ep.afterPropertiesSet();
+ ep.commence(request, response);
+ assertEquals("https://cas/login?renew=true&service=https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
+ response.getRedirect());
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterTests.java
new file mode 100644
index 0000000000..b965deaee4
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterTests.java
@@ -0,0 +1,93 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.MockAuthenticationManager;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpSession;
+
+
+/**
+ * Tests {@link CasProcessingFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CasProcessingFilterTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public CasProcessingFilterTests() {
+ super();
+ }
+
+ public CasProcessingFilterTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(CasProcessingFilterTests.class);
+ }
+
+ public void testGetters() {
+ CasProcessingFilter filter = new CasProcessingFilter();
+ assertEquals("/j_acegi_cas_security_check",
+ filter.getDefaultFilterProcessesUrl());
+ }
+
+ public void testNormalOperation() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest(null,
+ new MockHttpSession());
+ request.setParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(true);
+
+ CasProcessingFilter filter = new CasProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.init(null);
+
+ Authentication result = filter.attemptAuthentication(request);
+ assertTrue(result != null);
+ }
+
+ public void testNullServiceTicketHandledGracefully()
+ throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest(null,
+ new MockHttpSession());
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(false);
+
+ CasProcessingFilter filter = new CasProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.init(null);
+
+ try {
+ filter.attemptAuthentication(request);
+ fail("Should have thrown AuthenticationException");
+ } catch (AuthenticationException expected) {
+ assertTrue(true);
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/ui/cas/ServicePropertiesTests.java b/core/src/test/java/org/acegisecurity/ui/cas/ServicePropertiesTests.java
new file mode 100644
index 0000000000..e1ffb755bf
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/ui/cas/ServicePropertiesTests.java
@@ -0,0 +1,71 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.cas;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests {@link ServiceProperties}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ServicePropertiesTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public ServicePropertiesTests() {
+ super();
+ }
+
+ public ServicePropertiesTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(ServicePropertiesTests.class);
+ }
+
+ public void testDetectsMissingLoginFormUrl() throws Exception {
+ ServiceProperties sp = new ServiceProperties();
+
+ try {
+ sp.afterPropertiesSet();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertEquals("service must be specified", expected.getMessage());
+ }
+ }
+
+ public void testGettersSetters() throws Exception {
+ ServiceProperties sp = new ServiceProperties();
+ sp.setSendRenew(false);
+ assertFalse(sp.isSendRenew());
+ sp.setSendRenew(true);
+ assertTrue(sp.isSendRenew());
+
+ sp.setService("https://mycompany.com/service");
+ assertEquals("https://mycompany.com/service", sp.getService());
+
+ sp.afterPropertiesSet();
+ }
+}
diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml
index 851885eeb1..9d6b0b70ab 100644
--- a/docs/reference/src/index.xml
+++ b/docs/reference/src/index.xml
@@ -294,7 +294,7 @@
handling each request. Handling involves a number of
operations:
-
+
+
+
+ Login to CAS failed!
+
+
+ Your CAS credentials were rejected.
+ Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
+
+
+
+
diff --git a/samples/contacts/etc/cas/web.xml b/samples/contacts/etc/cas/web.xml
new file mode 100644
index 0000000000..fe02e4c524
--- /dev/null
+++ b/samples/contacts/etc/cas/web.xml
@@ -0,0 +1,160 @@
+
+
+
+
+
+