Initial CAS support.

This commit is contained in:
Ben Alex 2004-04-19 07:34:32 +00:00
parent 782bfe5a74
commit fa9b872570
60 changed files with 5561 additions and 17 deletions

View File

@ -21,5 +21,8 @@
<classpathentry kind="lib" path="lib/regexp/jakarta-oro.jar"/>
<classpathentry kind="lib" path="lib/jakarta-commons/commons-codec.jar"/>
<classpathentry kind="lib" path="lib/hsqldb/hsqldb.jar"/>
<classpathentry kind="lib" path="lib/cas/cas.jar"/>
<classpathentry kind="lib" path="lib/cas/casclient.jar"/>
<classpathentry kind="lib" path="lib/ehcache/ehcache.jar"/>
<classpathentry kind="output" path="target/eclipseclasses"/>
</classpath>

View File

@ -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.adapters.cas;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import javax.servlet.ServletRequest;
/**
* Provides actual CAS authentication by delegation to an
* <code>AuthenticationManager</code>.
*
* <P>
* Do not use this class directly. Instead configure CAS to use the {@link
* CasPasswordHandlerProxy}.
* </p>
*
* @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 by <code>CasPasswordHandlerProxy</code> for individual
* authentication requests.
*
* <P>
* Delegates to the configured <code>AuthenticationManager</code>.
* </p>
*
* @param servletRequest as provided by CAS
* @param username provided to CAS
* @param password provided to CAS
*
* @return whether authentication was successful or not
*/
public boolean authenticate(ServletRequest servletRequest, String username,
String password) {
if ((username == null) || "".equals(username)) {
return false;
}
if (password == null) {
password = "";
}
Authentication request = new UsernamePasswordAuthenticationToken(username
.toString(), password.toString());
Authentication response = null;
try {
response = authenticationManager.authenticate(request);
} catch (AuthenticationException failed) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user: " + username
+ " failed: " + failed.toString());
}
return false;
}
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user: " + username
+ " successful");
}
return true;
}
}

View File

@ -0,0 +1,142 @@
/* 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.adapters.cas;
import edu.yale.its.tp.cas.auth.PasswordHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* Enables CAS to use the Acegi Security System for authentication.
*
* <P>
* 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.
* </p>
*
* <p>
* It should be noted that the Acegi Security System will operate as a CAS
* client irrespective of the <code>PasswordHandler</code> used on the CAS
* server. In other words, this class need <B>not</B> 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
* <code>AuthenticationManager</code>.
* </p>
*
* <p>
* This class works requires a properly configured
* <code>CasPasswordHandler</code>. On the first authentication request, the
* class will use Spring's {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* method to obtain an <code>ApplicationContext</code> instance, inside which
* must be a configured <code>CasPasswordHandler</code> instance. The
* <code>CasPasswordHandlerProxy</code> will then delegate authentication
* requests to that instance.
* </p>
*
* <p>
* To configure CAS to use this class, edit CAS' <code>web.xml</code> and
* define the <code>edu.yale.its.tp.cas.authHandler</code> context parameter
* with the value
* <code>net.sf.acegisecurity.adapters.cas.CasPasswordHandlerProxy</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class CasPasswordHandlerProxy implements PasswordHandler {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(CasPasswordHandlerProxy.class);
//~ Instance fields ========================================================
private ApplicationContext ctx;
private CasPasswordHandler handler;
//~ Methods ================================================================
/**
* Called by CAS when authentication is required.
*
* <P>
* Delegates to the <code>CasPasswordHandler</code>.
* </p>
*
* @param request as provided by CAS
* @param username provided to CAS
* @param password provided to CAS
*
* @return whether authentication was successful or not
*
* @throws IllegalArgumentException if the application context does not
* contain a <code>CasPasswordHandler</code> or the
* <code>ServletRequest</code> was not of type
* <code>HttpServletRequest</code>
*/
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
* <code>ServletContext</code>
*
* @return the Spring application context
*/
protected ApplicationContext getContext(HttpServletRequest httpRequest) {
return WebApplicationContextUtils.getRequiredWebApplicationContext(httpRequest.getSession()
.getServletContext());
}
}

View File

@ -0,0 +1,7 @@
<html>
<body>
Adapter to Yale Central Authentication Service (CAS).
<p>
</body>
</html>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* 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.
*
* Demonstration of the applicationContext.xml that should be placed in
* CAS' WEB-INF directory. Note the CasPasswordHandler bean.
*
* $Id$
-->
<beans>
<!-- Data access object which stores authentication information -->
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLES_IGNORED_BY_CAS
dianne=emu,ROLES_IGNORED_BY_CAS
scott=wombat,ROLES_IGNORED_BY_CAS
peter=opal,disabled,ROLES_IGNORED_BY_CAS
</value>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
</bean>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="casPasswordHandler" class="net.sf.acegisecurity.adapters.cas.CasPasswordHandler">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
</bean>
</beans>

View File

@ -0,0 +1,112 @@
/* 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.adapters.cas;
import junit.framework.TestCase;
import net.sf.acegisecurity.MockHttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.http.HttpServletRequest;
/**
* Tests {@link CasPasswordHandlerProxy}.
*
* @author Ben Alex
* @version $Id$
*/
public class CasPasswordHandlerProxyTests extends TestCase {
//~ Constructors ===========================================================
public CasPasswordHandlerProxyTests() {
super();
}
public CasPasswordHandlerProxyTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(CasPasswordHandlerProxyTests.class);
}
public void testDetectsIfHttpServletRequestNotPassed() {
CasPasswordHandlerProxy proxy = new MockCasPasswordHandlerProxy(
"net/sf/acegisecurity/adapters/cas/applicationContext-valid.xml");
try {
proxy.authenticate(null, "x", "y");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("Can only process HttpServletRequest",
expected.getMessage());
}
}
public void testDetectsMissingDelegate() {
CasPasswordHandlerProxy proxy = new MockCasPasswordHandlerProxy(
"net/sf/acegisecurity/adapters/cas/applicationContext-invalid.xml");
try {
proxy.authenticate(new MockHttpServletRequest("/"), "x", "y");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("Bean context must contain at least one bean of type CasPasswordHandler",
expected.getMessage());
}
}
public void testNormalOperation() {
CasPasswordHandlerProxy proxy = new MockCasPasswordHandlerProxy(
"net/sf/acegisecurity/adapters/cas/applicationContext-valid.xml");
assertTrue(proxy.authenticate(new MockHttpServletRequest("/"),
"marissa", "koala"));
assertFalse(proxy.authenticate(new MockHttpServletRequest("/"),
"marissa", "WRONG_PASSWORD"));
assertFalse(proxy.authenticate(new MockHttpServletRequest("/"),
"INVALID_USER_NAME", "WRONG_PASSWORD"));
}
//~ Inner Classes ==========================================================
/**
* Mock object so that application context source can be specified.
*/
private class MockCasPasswordHandlerProxy extends CasPasswordHandlerProxy {
private ApplicationContext ctx;
public MockCasPasswordHandlerProxy(String appContextLocation) {
ctx = new ClassPathXmlApplicationContext(appContextLocation);
}
private MockCasPasswordHandlerProxy() {
super();
}
protected ApplicationContext getContext(HttpServletRequest httpRequest) {
return ctx;
}
}
}

View File

@ -0,0 +1,107 @@
/* 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.adapters.cas;
import junit.framework.TestCase;
import net.sf.acegisecurity.MockAuthenticationManager;
import net.sf.acegisecurity.MockHttpServletRequest;
/**
* Tests {@link CasPasswordHandler}.
*
* @author Ben Alex
* @version $Id$
*/
public class CasPasswordHandlerTests extends TestCase {
//~ Constructors ===========================================================
public CasPasswordHandlerTests() {
super();
}
public CasPasswordHandlerTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(CasPasswordHandlerTests.class);
}
public void testDeniesAccessWhenAuthenticationManagerThrowsException()
throws Exception {
CasPasswordHandler handler = new CasPasswordHandler();
handler.setAuthenticationManager(new MockAuthenticationManager(false));
handler.afterPropertiesSet();
assertFalse(handler.authenticate(new MockHttpServletRequest("/"),
"username", "password"));
}
public void testDetectsEmptyAuthenticationManager()
throws Exception {
CasPasswordHandler handler = new CasPasswordHandler();
try {
handler.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("An AuthenticationManager is required",
expected.getMessage());
}
}
public void testGettersSetters() {
CasPasswordHandler handler = new CasPasswordHandler();
handler.setAuthenticationManager(new MockAuthenticationManager(false));
assertTrue(handler.getAuthenticationManager() != null);
}
public void testGracefullyHandlesEmptyUsernamesAndPassword()
throws Exception {
CasPasswordHandler handler = new CasPasswordHandler();
handler.setAuthenticationManager(new MockAuthenticationManager(true));
handler.afterPropertiesSet();
// If empty or null username we return false
assertFalse(handler.authenticate(new MockHttpServletRequest("/"), "",
"password"));
assertFalse(handler.authenticate(new MockHttpServletRequest("/"), null,
"password"));
// We authenticate with null passwords (they might not have one)
assertTrue(handler.authenticate(new MockHttpServletRequest("/"),
"user", null));
assertTrue(handler.authenticate(new MockHttpServletRequest("/"),
"user", ""));
}
public void testNormalOperation() throws Exception {
CasPasswordHandler handler = new CasPasswordHandler();
handler.setAuthenticationManager(new MockAuthenticationManager(true));
handler.afterPropertiesSet();
assertTrue(handler.authenticate(new MockHttpServletRequest("/"),
"username", "password"));
}
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* 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.
*
* $Id$
-->
<beans>
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,ROLE_TELLER
scott=wombat,ROLE_TELLER
peter=opal,disabled,ROLE_TELLER
</value>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
</bean>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- No CasPasswordHandler -->
</beans>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* 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.
*
* $Id$
-->
<beans>
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,ROLE_TELLER
scott=wombat,ROLE_TELLER
peter=opal,disabled,ROLE_TELLER
</value>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
</bean>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="casPasswordHandler" class="net.sf.acegisecurity.adapters.cas.CasPasswordHandler">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
</bean>
</beans>

View File

@ -0,0 +1,220 @@
/* 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.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import net.sf.acegisecurity.ui.cas.CasProcessingFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* An {@link AuthenticationProvider} implementation that integrates with Yale
* Central Authentication Service (CAS).
*
* <p>
* This <code>AuthenticationProvider</code> is capable of validating {@link
* UsernamePasswordAuthenticationToken} requests which contain a
* <code>principal</code> name equal to either {@link
* CasProcessingFilter#CAS_STATEFUL_IDENTIFIER} or {@link
* CasProcessingFilter#CAS_STATELESS_IDENTIFIER}. It can also validate a
* previously created {@link CasAuthenticationToken}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class CasAuthenticationProvider implements AuthenticationProvider,
InitializingBean {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
//~ Instance fields ========================================================
private CasAuthoritiesPopulator casAuthoritiesPopulator;
private CasProxyDecider casProxyDecider;
private StatelessTicketCache statelessTicketCache;
private String key;
private TicketValidator ticketValidator;
//~ Methods ================================================================
public void setCasAuthoritiesPopulator(
CasAuthoritiesPopulator casAuthoritiesPopulator) {
this.casAuthoritiesPopulator = casAuthoritiesPopulator;
}
public CasAuthoritiesPopulator getCasAuthoritiesPopulator() {
return casAuthoritiesPopulator;
}
public void setCasProxyDecider(CasProxyDecider casProxyDecider) {
this.casProxyDecider = casProxyDecider;
}
public CasProxyDecider getCasProxyDecider() {
return casProxyDecider;
}
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void setStatelessTicketCache(
StatelessTicketCache statelessTicketCache) {
this.statelessTicketCache = statelessTicketCache;
}
public StatelessTicketCache getStatelessTicketCache() {
return statelessTicketCache;
}
public void setTicketValidator(TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public TicketValidator getTicketValidator() {
return ticketValidator;
}
public void afterPropertiesSet() throws Exception {
if (this.casAuthoritiesPopulator == null) {
throw new IllegalArgumentException(
"A casAuthoritiesPopulator must be set");
}
if (this.ticketValidator == null) {
throw new IllegalArgumentException("A ticketValidator must be set");
}
if (this.casProxyDecider == null) {
throw new IllegalArgumentException("A casProxyDecider must be set");
}
if (this.statelessTicketCache == null) {
throw new IllegalArgumentException(
"A statelessTicketCache must be set");
}
if (key == null) {
throw new IllegalArgumentException(
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
}
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
if (authentication instanceof UsernamePasswordAuthenticationToken
&& (!CasProcessingFilter.CAS_STATEFUL_IDENTIFIER.equals(
authentication.getPrincipal().toString())
&& !CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(
authentication.getPrincipal().toString()))) {
// UsernamePasswordAuthenticationToken not CAS related
return null;
}
// If an existing CasAuthenticationToken, just check we created it
if (authentication instanceof CasAuthenticationToken) {
if (this.key.hashCode() == ((CasAuthenticationToken) authentication)
.getKeyHash()) {
return authentication;
} else {
throw new BadCredentialsException(
"The presented CasAuthenticationToken does not contain the expected key");
}
}
// Ensure credentials are presented
if (authentication.getCredentials() == null || "".equals(authentication.getCredentials())) {
throw new BadCredentialsException(
"Failed to provide a CAS service ticket to validate");
}
boolean stateless = false;
if (authentication instanceof UsernamePasswordAuthenticationToken
&& CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(
authentication.getPrincipal())) {
stateless = true;
}
CasAuthenticationToken result = null;
if (stateless) {
// Try to obtain from cache
result = statelessTicketCache.getByTicketId(authentication.getCredentials()
.toString());
}
if (result == null) {
result = this.authenticateNow(authentication);
}
if (stateless) {
// Add to cache
statelessTicketCache.putTicketInCache(result);
}
return result;
}
public boolean supports(Class authentication) {
if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(
authentication)) {
return true;
} else if (CasAuthenticationToken.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
private CasAuthenticationToken authenticateNow(
Authentication authentication) throws AuthenticationException {
// Validate
TicketResponse response = ticketValidator.confirmTicketValid(authentication.getCredentials()
.toString());
// Check proxy list is trusted
this.casProxyDecider.confirmProxyListTrusted(response.getProxyList());
// Build list of granted authorities
GrantedAuthority[] ga = this.casAuthoritiesPopulator.getAuthorities(response
.getUser());
// Construct CasAuthenticationToken
return new CasAuthenticationToken(this.key, response.getUser(),
authentication.getCredentials(), ga, response.getProxyList(),
response.getProxyGrantingTicketIou());
}
}

View File

@ -0,0 +1,172 @@
/* 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.GrantedAuthority;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
import java.io.Serializable;
import java.util.List;
/**
* Represents a successful CAS <code>Authentication</code>.
*
* @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 <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from {@link
* CasAuthoritiesPopulator}) (cannot be <code>null</code>)
* @param proxyList the list of proxies from CAS (cannot be
* <code>null</code>)
* @param proxyGrantingTicketIou the PGT-IOU ID from CAS (cannot be
* <code>null</code>)
*
* @throws IllegalArgumentException if a <code>null</code> 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 <code>true</code>).
*
* @param isAuthenticated ignored
*/
public void setAuthenticated(boolean isAuthenticated) {
// ignored
}
/**
* Always returns <code>true</code>.
*
* @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();
}
}

View File

@ -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 <code>GrantedAuthority[]</code> objects for a CAS
* authenticated user.
*
* <P>
* 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
* <code>Authentication</code> object, implementations of this interface will
* provide this information.
* </p>
*
* <P>
* Implementations should not perform any caching. They will only be called
* when a refresh is required.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface CasAuthoritiesPopulator {
//~ Methods ================================================================
/**
* Obtains the granted authorities for the specified user.
*
* <P>
* May throw any <code>AuthenticationException</code> or return
* <code>null</code> if the authorities are unavailable.
* </p>
*
* @param casUserId as obtained from the CAS validation service
*
* @return the granted authorities for the indicated user
*/
public GrantedAuthority[] getAuthorities(String casUserId)
throws AuthenticationException;
}

View File

@ -0,0 +1,72 @@
/* 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;
/**
* Decides whether a proxy list presented via CAS is trusted or not.
*
* <P>
* 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.
* </p>
*
* <P>
* 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.
* </p>
*
* <P>
* If this application is a target service of a proxy ticket, the
* <code>CasProxyDecider</code> resolves whether or not the proxy list is
* trusted. Applications should only trust services they allow to impersonate
* an end user.
* </p>
*
* <P>
* 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).
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface CasProxyDecider {
//~ Methods ================================================================
/**
* Decides whether the proxy list is trusted.
*
* <P>
* Must throw any <code>ProxyUntrustedException</code> if the proxy list is
* untrusted.
* </p>
*/
public void confirmProxyListTrusted(List proxyList)
throws ProxyUntrustedException;
}

View File

@ -0,0 +1,50 @@
/* 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;
/**
* Thrown if a CAS proxy ticket is presented from an untrusted proxy.
*
* @author Ben Alex
* @version $Id$
*/
public class ProxyUntrustedException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>ProxyUntrustedException</code> with the specified
* message.
*
* @param msg the detail message.
*/
public ProxyUntrustedException(String msg) {
super(msg);
}
/**
* Constructs a <code>ProxyUntrustedException</code> 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);
}
}

View File

@ -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.
*
* <p>
* 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 <code>HttpSession</code>. This allows the
* affirmative CAS validation outcome to be stored in the
* <code>HttpSession</code>, meaning the removal of the ticket from the CAS
* server is not an issue issue.
* </p>
*
* <P>
* Stateless callers, such as remoting protocols, cannot take advantage of
* <code>HttpSession</code>. 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.
* </p>
*
* <P>
* 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 <code>HttpSession</code> is available for such callers, the
* affirmative CAS validation outcome cannot be stored in this location.
* </p>
*
* <P>
* The <code>StatelessTicketCache</code> enables the service tickets and proxy
* tickets belonging to stateless callers to be placed in a cache. This
* in-memory cache stores the <code>CasAuthenticationToken</code>, effectively
* providing the same capability as a <code>HttpSession</code> with the ticket
* identifier being the key rather than a session identifier.
* </p>
*
* <P>
* 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.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface StatelessTicketCache {
//~ Methods ================================================================
/**
* Retrieves the <code>CasAuthenticationToken</code> associated with the
* specified ticket.
*
* <P>
* If not found, returns a
* <code>null</code><code>CasAuthenticationToken</code>.
* </p>
*
* @return the fully populated authentication token
*/
public CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
*
* <P>
* The {@link CasAuthenticationToken#getCredentials()} method is used to
* retrieve the service ticket number.
* </p>
*
* @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)}.
*
* <P>
* Implementations should use {@link
* CasAuthenticationToken#getCredentials()} to obtain the ticket and then
* delegate to to the {@link #removeTicketFromCache(String)} method.
* </p>
*
* @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.
*
* <P>
* This is in case applications wish to provide a session termination
* capability for their stateless clients.
* </p>
*
* @param serviceTicket to be removed
*/
public void removeTicketFromCache(String serviceTicket);
}

View File

@ -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.
*
* <P>
* If <code>null</code> is passed into the <code>proxyList</code> or
* <code>proxyGrantingTicketIou</code>, suitable defaults are established.
* However, <code>null</code> cannot be passed for the <code>user</code>
* argument.
* </p>
*
* @param user the user as indicated by CAS (cannot be <code>null</code> or
* an empty <code>String</code>)
* @param proxyList as provided by CAS (may be <code>null</code>)
* @param proxyGrantingTicketIou as provided by CAS (may be
* <code>null</code>)
*
* @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();
}
}

View File

@ -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.
*
* <P>
* 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 <code>TicketValidator</code>).
* </p>
*
* <P>
* Implementations may request a proxy granting ticket if wish, although this
* behaviour is not mandatory.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface TicketValidator {
//~ Methods ================================================================
/**
* Returns information about the ticket, if it is valid for this service.
*
* <P>
* Must throw an <code>AuthenticationException</code> if the ticket is not
* valid for this service.
* </p>
*
* @return details of the CAS service ticket
*/
public TicketResponse confirmTicketValid(String serviceTicket)
throws AuthenticationException;
}

View File

@ -0,0 +1,108 @@
/* 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 net.sf.acegisecurity.providers.cas.CasAuthenticationToken;
import net.sf.acegisecurity.providers.cas.StatelessTicketCache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Caches tickets using <A HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedTicketCache implements StatelessTicketCache,
InitializingBean {
//~ Instance fields ========================================================
private Cache cache;
private CacheManager manager;
private int minutesToIdle = 20;
//~ Methods ================================================================
public CasAuthenticationToken getByTicketId(String serviceTicket) {
Element element = null;
try {
element = cache.get(serviceTicket);
} catch (CacheException cacheException) {
throw new DataRetrievalFailureException("Cache failure: "
+ cacheException.getMessage());
}
if (element == null) {
System.out.println("not found");
return null;
} else {
System.out.println("found");
return (CasAuthenticationToken) element.getValue();
}
}
public void setMinutesToIdle(int minutesToIdle) {
this.minutesToIdle = minutesToIdle;
}
/**
* Specifies how many minutes an entry will remain in the cache from when
* it was last accessed. This is effectively the session duration.
*
* <P>
* Defaults to 20 minutes.
* </p>
*
* @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);
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
An authentication provider that can process Yale Central Authentication Service (CAS)
service tickets and proxy tickets.
</body>
</html>

View File

@ -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}.
*
* <P>
* The additional information (username, password, enabled status etc) an
* <code>AuthenticationDao</code> implementation provides about a
* <code>User</code> is ignored. Only the <code>GrantedAuthority</code>s are
* relevant to this class.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class DaoCasAuthoritiesPopulator implements CasAuthoritiesPopulator,
InitializingBean {
//~ Instance fields ========================================================
private AuthenticationDao authenticationDao;
//~ Methods ================================================================
public void setAuthenticationDao(AuthenticationDao authenticationDao) {
this.authenticationDao = authenticationDao;
}
public AuthenticationDao getAuthenticationDao() {
return authenticationDao;
}
public GrantedAuthority[] getAuthorities(String casUserId)
throws AuthenticationException {
return this.authenticationDao.loadUserByUsername(casUserId)
.getAuthorities();
}
public void afterPropertiesSet() throws Exception {
if (this.authenticationDao == null) {
throw new IllegalArgumentException(
"An authenticationDao must be set");
}
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
Implementations that populate GrantedAuthority[]s of CAS authentications.
</body>
</html>

View File

@ -0,0 +1,55 @@
/* 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 a proxied request from any other service.
*
* <P>
* Also accepts the request if there was no proxy (ie the user directly
* authenticated against this service).
* </p>
*
* @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());
}
}
}

View File

@ -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
* <code>validProxies</code> list.
*
* <P>
* Also accepts the request if there was no proxy (ie the user directly
* authenticated against this service).
* </p>
*
* @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");
}
}
}

View File

@ -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.
*
* <P>
* This class should be used if only service tickets wish to be accepted (ie no
* proxy tickets at all).
* </p>
*
* @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");
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
Implementations that decide whether proxy lists of
CAS authentications are trusted.
</body>
</html>

View File

@ -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 for <code>TicketValidator</code>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.
*
* <P>
* This is usually something like
* <code>https://www.mycompany.com/cas/proxyValidate</code>.
* </p>
*
* @return the CAS proxy ticket validation URL
*/
public String getCasValidate() {
return casValidate;
}
public void setServiceProperties(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public ServiceProperties getServiceProperties() {
return serviceProperties;
}
public void setTrustStore(String trustStore) {
this.trustStore = trustStore;
}
/**
* Optional property which will be used to set the system property
* <code>javax.net.ssl.trustStore</code>.
*
* @return the <code>javax.net.ssl.trustStore</code> that will be set
* during bean initialization, or <code>null</code> 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);
}
}
}

View File

@ -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' <code>ProxyTicketValidator</code> 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.
*
* <P>
* This callback URL belongs to the Acegi Security System for Spring
* secured application. We suggest you use CAS'
* <code>ProxyTicketReceptor</code> servlet to receive this callback and
* manage the proxy-granting ticket list. The callback URL is usually
* something like
* <code>https://www.mycompany.com/application/casProxy/receptor</code>.
* </p>
*
* <P>
* If left <code>null</code>, the <code>CasAuthenticationToken</code> 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.
* </p>
*
* @return the proxy callback URL, or <code>null</code> 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 <code>ProxyTicketValidator</code>
*
* @return the <code>TicketResponse</code>
*
* @throws AuthenticationServiceException
* if<code>ProxyTicketValidator</code> 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());
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
Implementations that validate service tickets.
</body>
</html>

View File

@ -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.
*
* <p>
* 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 <code>service</code>. The opal ticket
* string is presented in the <code>ticket</code> request parameter. This
* filter monitors the <code>service</code> URL so it can receive the service
* ticket and process it. The CAS server knows which <code>service</code> URL
* to use via the {@link ServiceProperties#getService()} method.
* </p>
*
* <P>
* Processing the service ticket involves creating a
* <code>UsernamePasswordAuthenticationToken</code> which uses {@link
* #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque
* ticket string as the <code>credentials</code>.
* </p>
*
* <P>
* The configured <code>AuthenticationManager</code> is expected to provide a
* provider that can recognise
* <code>UsernamePasswordAuthenticationToken</code>s containing this special
* <code>principal</code> name, and process them accordingly by validation
* with the CAS server.
* </p>
*
* <P>
* <B>Do not use this class directly.</B> Instead configure
* <code>web.xml</code> to use the {@link
* net.sf.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class CasProcessingFilter extends AbstractProcessingFilter {
//~ Static fields/initializers =============================================
/**
* Used to identify a CAS request for a stateful user agent, such as a web
* browser.
*/
public static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
/**
* Used to identify a CAS request for a stateless user agent, such as a
* remoting protocol client (eg Hessian, Burlap, SOAP etc). Results in a
* more aggressive caching strategy being used, as the absence of a
* <code>HttpSession</code> 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
* <code>/j_acegi_cas_security_check</code>.
*
* @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 {}
}

View File

@ -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 <code>SecurityEnforcementFilter</code> to commence
* authentication via the Yale Central Authentication Service (CAS).
*
* <P>
* The user's browser will be redirected to the Yale CAS enterprise-wide login
* page. This page is specified by the <code>loginUrl</code> property. Once
* login is complete, the CAS login page will redirect to the page indicated
* by the <code>service</code> property. The <code>service</code> is a HTTP
* URL belonging to the current application. The <code>service</code> URL is
* monitored by the {@link CasProcessingFilter}, which will validate the CAS
* login was successful.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint,
InitializingBean {
//~ Instance fields ========================================================
private ServiceProperties serviceProperties;
private String loginUrl;
//~ Methods ================================================================
public void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}
/**
* The enterprise-wide CAS login URL. Usually something like
* <code>https://www.mycompany.com/cas/login</code>.
*
* @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);
}
}

View File

@ -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.
*
* <P>
* 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.
* </p>
*
* @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 the <code>renew</code> parameter should be sent to the
* CAS login URL and CAS validation URL.
* <P> If <code>true</code>, 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 <code>true</code>.
* Defaults to <code>false</code>, providing automated single sign on.
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
public boolean isSendRenew() {
return sendRenew;
}
public void setService(String service) {
this.service = service;
}
/**
* Represents the service the user is authenticating to.
*
* <B>This service is the callback URL
* belonging to the local Acegi Security System for Spring secured
* application. For example,
* <code>https://www.mycompany.com/application/j_acegi_cas_security_check</code>
*
* @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");
}
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
Authenticates standard web browser users via
Yale Central Authentication Service (CAS).
</body>
</html>

View File

@ -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++) {

View File

@ -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;

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}
}
}

View File

@ -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
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");
}
}
}

View File

@ -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");
}
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -294,7 +294,7 @@
handling each request. Handling involves a number of
operations:</para>
<itemizedlist spacing="compact">
<orderedlist>
<listitem>
<para>Store the configuration attributes that are associated with
each secure request.</para>
@ -354,7 +354,7 @@
<para>Return any result received from the
<literal>SecurityInterceptorCallback</literal>.</para>
</listitem>
</itemizedlist>
</orderedlist>
<para>Whilst this may seem quite involved, don't worry. Developers
interact with the security process by simply implementing basic
@ -854,6 +854,13 @@
<literal>AuthenticationProvider</literal> if you were not using
container adapters.</para>
</listitem>
<listitem>
<para><literal>CasAuthenticationProvider</literal> is able to
authenticate Yale Central Authentication Service (CAS) tickets.
This is discussed further in the CAS Single Sign On
section.</para>
</listitem>
</itemizedlist></para>
</sect2>
@ -870,8 +877,26 @@
<para><programlisting>&lt;bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;
&lt;property name="authenticationDao"&gt;&lt;ref bean="inMemoryDaoImpl"/&gt;&lt;/property&gt;
&lt;property name="saltSource"&gt;&lt;ref bean="saltSource"/&gt;&lt;/property&gt;
&lt;property name="passwordEncoder"&gt;&lt;ref bean="passwordEncoder"/&gt;&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>The <literal>PasswordEncoder</literal> and
<literal>SaltSource</literal> are optional. A
<literal>PasswordEncoder</literal> provides encoding and decoding of
passwords obtained from the authentication repository. A
<literal>SaltSource</literal> enables the passwords to be populated
with a "salt", which enhances the security of the passwords in the
authentication repository. <literal>PasswordEncoder</literal>
implementations are provided with the Acegi Security System for Spring
covering MD5, SHA and cleartext encodings. Two
<literal>SaltSource</literal> implementations are also provided:
<literal>SystemWideSaltSource</literal> which encodes all passwords
with the same salt, and <literal>ReflectionSaltSource</literal>, which
inspects a given property of the returned User object to obtain the
salt. Please refer to the JavaDocs for further details on these
optional features.</para>
<para>For a class to be able to provide the
<literal>DaoAuthenticationProvider</literal> with access to an
authentication repository, it must implement the
@ -957,10 +982,13 @@
<para>You can use different relational database management systems by
modifying the <literal>DriverManagerDataSource</literal> shown above.
Irrespective of the database used, a standard schema must be used as
indicated in <literal>dbinit.txt</literal>. Of particular note is the
database must return responses that treat the username as case
insensitive, in order to comply with the
<literal>AuthenticationDao</literal> contract.</para>
indicated in <literal>dbinit.txt</literal>.</para>
<para>If you default schema is unsuitable for your needs,
<literal>JdbcDaoImpl</literal> provides two properties that allow
customisation of the SQL statements. You may also subclass the
<literal>JdbcDaoImpl</literal> if further customisation is necessary.
Please refer to the JavaDocs for details.</para>
<para>The Acegi Security System for Spring ships with a Hypersonic SQL
instance that has the required authentication information and sample
@ -1487,11 +1515,13 @@ public boolean supports(Class clazz);</programlisting></para>
<literal>HttpSession</literal> object and filters to authenticate the
user. Another approach is HTTP Basic Authentication, which allows
clients to use HTTP headers to present authentication information to
the Acegi Security System for Spring. The final approach is via
Container Adapters, which allow supported web containers to perform
the authentication themselves. HTTP Session Authentication is
discussed below, whilst Container Adapters are discussed in a separate
section.</para>
the Acegi Security System for Spring. Alternatively, you can also use
Yale Central Authentication Service (CAS) for enterprise-wide single
sign on. The final approach is via Container Adapters, which allow
supported web containers to perform the authentication themselves.
HTTP Session and Basic Authentication is discussed below, whilst CAS
and Container Adapters are discussed in separate sections of this
document.</para>
</sect2>
<sect2 id="security-ui-http-session">
@ -1538,7 +1568,7 @@ public boolean supports(Class clazz);</programlisting></para>
<literal>authenticationFailureUrl</literal>. The
<literal>AuthenticationException</literal> will be placed into the
<literal>HttpSession</literal> attribute indicated by
<literal>AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY</literal>,
<literal>AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY</literal>,
enabling a reason to be provided to the user on the error page.</para>
<para>If authentication is successful, the resulting
@ -1552,7 +1582,7 @@ public boolean supports(Class clazz);</programlisting></para>
browser will need to be redirected to the target URL. The target URL
is usually indicated by the <literal>HttpSession</literal> attribute
specified by
<literal>AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY</literal>.
<literal>AbstractProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY</literal>.
This attribute is automatically set by the
<literal>SecurityEnforcementFilter</literal> when an
<literal>AuthenticationException</literal> occurs, so that after login
@ -2100,6 +2130,569 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
</sect2>
</sect1>
<sect1 id="security-cas">
<title>Yale Central Authentication Service (CAS) Single Sign On</title>
<sect2 id="security-cas-overview">
<title>Overview</title>
<para>Yale University produces an enterprise-wide single sign on
system known as CAS. Unlike other initiatives, Yale's Central
Authentication Service is open source, widely used, simple to
understand, platform independent, and supports proxy capabilities. The
Acegi Security System for Spring fully supports CAS, and provides an
easy migration path from single-application deployments of Acegi
Security through to multiple-application deployments secured by an
enterprise-wide CAS server.</para>
<para>You can learn more about CAS at
<literal>http://www.yale.edu/tp/auth/</literal>. You will need to
visit this URL to download the CAS Server files. Whilst the Acegi
Security System for Spring includes two CAS libraries in the
"-with-dependencies" ZIP file, you will still need the CAS Java Server
Pages and <literal>web.xml</literal> to customise and deploy your CAS
server.</para>
</sect2>
<sect2 id="security-cas-how-cas-works">
<title>How CAS Works</title>
<para>Whilst the CAS web site above contains two documents that detail
the architecture of CAS, we present the general overview again here
within the context of the Acegi Security System for Spring. The
following refers to CAS 2.0, being the version of CAS that Acegi
Security for Spring supports.</para>
<para>Somewhere in your enterprise you will need to setup a CAS
server. The CAS server is simply a standard WAR file, so there isn't
anything difficult about setting up your server. Inside the WAR file
you will customise the login and other single sign on pages displayed
to users. You will also need to specify in the web.xml a
<literal>PasswordHandler</literal>. The
<literal>PasswordHandler</literal> has a simple method that returns a
boolean as to whether a given username and password is valid. Your
<literal>PasswordHandler</literal> implementation will need to link
into some type of backend authentication repository, such as an LDAP
server or database. </para>
<para>If you're running an existing CAS server, you will have already
established a <literal>PasswordHandler</literal>. If you have not,
might prefer to use the Acegi Security System for Spring
<literal>CasPasswordHandler</literal> class. This class delegates
through to the standard Acegi Security
<literal>AuthenticationManager</literal>, enabling you to use a
security configuration you might already have in place. You do not
need to use the <literal>CasPasswordHandler</literal> class on your
CAS server unless you do not wish. The Acegi Security System for
Spring will function as a CAS client successfully irrespective of the
<literal>PasswordHandler</literal> you've chosen for your CAS
server.</para>
<para>Apart from the CAS server itself, the other key player is of
course the secure web applications deployed throughout your
enterprise. These web applications are known as "services". There are
two types of services: standard services and proxy services. A proxy
service is able to request resources from other services on behalf of
the user. This will be explained more fully later.</para>
<para>Services can be developed in a large variety of languages, due
to CAS 2.0's very light XML-based protocol. The Yale CAS home page
contains a clients archive which demonstrates CAS clients in Java,
Active Server Pages, Perl, Python and others. Naturally, Java support
is very strong given the CAS server is written in Java. You do not
need to use one of CAS' clients to interact with the CAS server from
Acegi Security System for Spring secured applications. This is handled
transparently for you.</para>
<para>The basic interaction between a web browser, CAS server and an
Acegi Security for System Spring secured service is as follows:</para>
<orderedlist>
<listitem>
<para>The web user is browsing the service's public pages. CAS or
Acegi Security is not involved.</para>
</listitem>
<listitem>
<para>The user eventually requests a page that is either secure or
one of the beans it uses is secure. Acegi Security's
SecurityEnforcementFilter will detect the
AuthenticationException.</para>
</listitem>
<listitem>
<para>Because the user has no <literal>Authentication</literal>
object in
<literal>HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY</literal>,
the SecurityEnforcementFilter will call the configured
<literal>AuthenticationEntryPoint</literal>. If using CAS, this
will be the <literal>CasProcessingFilterEntryPoint</literal>
class.</para>
</listitem>
<listitem>
<para>The CasProcessingFilterEntry point will redirect the user's
browser to the CAS server. It will also indicate a
<literal>service</literal> parameter, which is the callback URL
for the Acegi Security service. For example, the URL the browser
is redirected to might be
<literal>https://my.company.com/cas/login?service=https://server3.company.com/webapp/j_acegi_cas_security_check</literal>.</para>
</listitem>
<listitem>
<para>After the user's browser redirects to CAS, they will be
prompted for their username and password. If the user presents a
session cookie which indicates they've previously logged on, they
will not be prompted to login again (there is an exception to this
procedure, which we'll cover later). CAS will use the
PasswordHandler discussed above to decide whether the username and
password is valid</para>
</listitem>
<listitem>
<para>Upon successful login, CAS will redirect the user's browser
back to the original service. It will also include a
<literal>ticket</literal> parameter, which is an opaque string
representing the "service ticket". Continuing our earlier example,
the URL the browser is redirected to might be
<literal>https://server3.company.com/webapp/j_acegi_cas_security_check?ticket=ST-0-ER94xMJmn6pha35CQRoZ</literal>.</para>
</listitem>
<listitem>
<para>Back in the service web application, the
<literal>CasProcessingFilter</literal> is always listening for
requests to <literal>/j_acegi_cas_security_check</literal> (this
is configurable, but we'll use the defaults in this introduction).
The processing filter will construct a
<literal>UsernamePasswordAuthenticationToken</literal>
representing the service ticket. The principal will be equal to
<literal>CasProcessingFilter.CAS_STATEFUL_IDENTIFIER</literal>,
whilst the credentials will be the service ticket opaque value.
This authentication request will then be handed to the configured
<literal>AuthenticationManager</literal>.</para>
</listitem>
<listitem>
<para>The AuthenticationManager implementation will be the
<literal>ProviderManager</literal>, which is in turn configured
with the <literal>CasAuthenticationProvider</literal>. The
<literal>CasAuthenticationProvider</literal> only responds to
<literal>UsernamePasswordAuthenticationToken</literal>s containing
the CAS-specific principal (such as
<literal>CasProcessingFilter.CAS_STATEFUL_IDENTIFIER</literal>)
and <literal>CasAuthenticationToken</literal>s (discussed
later).</para>
</listitem>
<listitem>
<para><literal>CasAuthenticationProvider</literal> will validate
the service ticket using a <literal>TicketValidator</literal>
implementation. Acegi Security includes one implementation, the
<literal>CasProxyTicketValidator</literal>. This implementation
uses a CAS-supplied ticket validator. The
<literal>CasProxyTicketValidator</literal> makes a HTTPS request
to the CAS server in order to validate the service ticket. The
<literal>CasProxyTicketValidator</literal> may also include a
proxy callback parameter, which is included in this example:
<literal>https://my.company.com/cas/proxyValidate?service=https://server3.company.com/webapp/j_acegi_cas_security_check&amp;ticket=ST-0-ER94xMJmn6pha35CQRoZ&amp;pgtUrl=https://server3.company.com/webapp/casProxy/receptor</literal>.</para>
</listitem>
<listitem>
<para>Back of the CAS server, the proxy validation request will be
received. If the presented service ticket matches the service URL
requested initially, CAS will provide an affirmative response in
XML indicating the username. If any proxy was involved in the
authentication (discussed below), the list of proxies is also
included in the XML response.</para>
</listitem>
<listitem>
<para>[OPTIONAL] If the request to the CAS validation service
included the <literal>pgtUrl</literal>, CAS will include a
<literal>pgtIou</literal> string in the XML response. This
<literal>pgtIou</literal> represents a proxy-granting ticket IOU.
The CAS server will then create its own HTTPS connection back to
the <literal>pgtUrl</literal>. This is to mutually authenticate
the CAS server and the claimed service. The HTTPS connection will
be used to send a proxy granting ticket to the original web
application. For example,
<literal>https://server3.company.com/webapp/casProxy/receptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&amp;pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH</literal>.
We suggest you use CAS' <literal>ProxyTicketReceptor</literal>
servlet to receive these proxy-granting tickets, if they are
required.</para>
</listitem>
<listitem>
<para>The <literal>CasProxyTicketValidator</literal> will parse
the XML received from the CAS server. It will return to the
<literal>CasAuthenticationProvider</literal> a
<literal>TicketResponse</literal>, which includes the username
(mandatory), proxy list (if any were involved), and proxy-granting
ticket IOU (if the proxy callback was requested).</para>
</listitem>
<listitem>
<para>Next <literal>CasAuthenticationProvider</literal> will call
a configured <literal>CasProxyDecider</literal>. The
<literal>CasProxyDecider</literal> indicates whether the proxy
list in the <literal>TicketResponse</literal> is acceptable to the
service. Several implementations are provided with the Acegi
Security System: <literal>RejectProxyTickets</literal>,
<literal>AcceptAnyCasProxy</literal> and
<literal>NamedCasProxyDecider</literal>. These names are largely
self-explanatory, except <literal>NamedCasProxyDecider</literal>
which allows a <literal>List</literal> of trusted proxies to be
provided.</para>
</listitem>
<listitem>
<para><literal>CasAuthenticationProvider</literal> will next
request a <literal>CasAuthoritiesPopulator</literal> to advise the
<literal>GrantedAuthority</literal> objects that apply to the user
contained in the <literal>TicketResponse</literal>. Acegi Security
includes a <literal>DaoCasAuthoritiesPopulator</literal> which
simply uses the <literal>AuthenticationDao</literal>
infrastructure to find the <literal>User</literal> and their
associated <literal>GrantedAuthority</literal>s. Note that the
password and enabled/disabled status of <literal>User</literal>s
returned by the <literal>AuthenticationDao</literal> are ignored,
as the CAS server is responsible for authentication decisions.
<literal>DaoCasAuthoritiesPopulator</literal> is only concerned
with retrieving the <literal>GrantedAuthority</literal>s.</para>
</listitem>
<listitem>
<para>If there were no problems,
<literal>CasAuthenticationProvider</literal> constructs a
<literal>CasAuthenticationToken</literal> including the details
contained in the <literal>TicketResponse</literal> and the
<literal>GrantedAuthority</literal>s. The
<literal>CasAuthenticationToken</literal> contains the hash of a
key, so that the <literal>CasAuthenticationProvider</literal>
knows it created it.</para>
</listitem>
<listitem>
<para>Control then returns to
<literal>CasProcessingFilter</literal>, which places the created
<literal>CasAuthenticationToken</literal> into the
<literal>HttpSession</literal> attribute named
<literal>HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY</literal>.</para>
</listitem>
<listitem>
<para>The user's browser is redirected to the original page that
caused the <literal>AuthenticationException</literal>.</para>
</listitem>
<listitem>
<para>As the <literal>Authentication</literal> object is now in
the well-known location, it is handled like any other
authentication approach. Usually the
<literal>AutoIntegrationFilter</literal> will be used to associate
the <literal>Authentication</literal> object with the
<literal>ContextHolder</literal> for the duration of each
request.</para>
</listitem>
</orderedlist>
<para>It's good that you're still here! It might sound involved, but
you can relax as the Acegi Security System for Spring classes hide
much of the complexity. Let's now look at how this is
configured.</para>
</sect2>
<sect2 id="security-cas-install-server">
<title>CAS Server Installation (Optional)</title>
<para>As mentioned above, the Acegi Security System for Spring
includes a <literal>PasswordHandler</literal> that bridges your
existing <literal>AuthenticationManager</literal> into CAS. You do not
need to use this <literal>PasswordHandler</literal> to use Acegi
Security on the client side (any CAS
<literal>PasswordHandler</literal> will do).</para>
<para>To install, you will need to download and extract the CAS server
archive. We used version 2.0.12 Beta 3. There will be a
<literal>/web</literal> directory in the root of the deployment. Copy
an <literal>applicationContext.xml</literal> containing your
<literal>AuthenticationManager</literal> as well as the
<literal>CasPasswordHandler</literal> into the
<literal>/web/WEB-INF</literal> directory. A sample
<literal>applicationContext.xml</literal> is included below:</para>
<programlisting>&lt;bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl"&gt;
&lt;property name="userMap"&gt;
&lt;value&gt;
marissa=koala,ROLES_IGNORED_BY_CAS
dianne=emu,ROLES_IGNORED_BY_CAS
scott=wombat,ROLES_IGNORED_BY_CAS
peter=opal,disabled,ROLES_IGNORED_BY_CAS
&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;
&lt;property name="authenticationDao"&gt;&lt;ref bean="inMemoryDaoImpl"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;
&lt;property name="providers"&gt;
&lt;list&gt;
&lt;ref bean="daoAuthenticationProvider"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casPasswordHandler" class="net.sf.acegisecurity.adapters.cas.CasPasswordHandler"&gt;
&lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
&lt;/bean&gt;</programlisting>
<para>Note the granted authorities are ignored by CAS. It has no way
of communciating the granted authorities to calling applications. CAS
is only concerned with username and passwords.</para>
<para>Next you will need to edit the existing
<literal>/web/WEB-INF/web.xml</literal> file. Add (or edit in the case
of the <literal>authHandler</literal> property) the following
lines:</para>
<para><programlisting>&lt;context-param&gt;
&lt;param-name&gt;edu.yale.its.tp.cas.authHandler&lt;/param-name&gt;
&lt;param-value&gt;net.sf.acegisecurity.adapters.cas.CasPasswordHandlerProxy&lt;/param-value&gt;
&lt;/context-param&gt;
&lt;context-param&gt;
&lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
&lt;param-value&gt;/WEB-INF/applicationContext.xml&lt;/param-value&gt;
&lt;/context-param&gt;
&lt;listener&gt;
&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
&lt;/listener&gt;</programlisting></para>
<para>Copy the <literal>spring.jar</literal> and
<literal>acegi-security.jar</literal> files into
<literal>/web/WEB-INF/lib</literal>. Now use the <literal>ant
dist</literal> task in the <literal>build.xml</literal> in the root of
the directory structure. This will create
<literal>/lib/cas.war</literal>, which is ready for deployment to your
servlet container.</para>
</sect2>
<sect2 id="security-cas-install-client">
<title>CAS Acegi Security System Client Installation</title>
<para>The web application side of CAS is made easy due to the Acegi
Security System for Spring. It is assumed you already know the basics
of using the Acegi Security System for Spring, so these are not
covered again below. Only the CAS-specific beans are mentioned.</para>
<para>You will need to add a <literal>ServiceProperties</literal> bean
to your application context. This represents your service:</para>
<para><programlisting>&lt;bean id="serviceProperties" class="net.sf.acegisecurity.ui.cas.ServiceProperties"&gt;
&lt;property name="service"&gt;&lt;value&gt;https://localhost:8443/contacts-cas/j_acegi_cas_security_check&lt;/value&gt;&lt;/property&gt;
&lt;property name="sendRenew"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>The <literal>service</literal> must equal a URL that will be
monitored by the <literal>CasProcessingFilter</literal>. The
<literal>sendRenew</literal> defaults to false, but should be set to
true if your application is particularly sensitive. What this
parameter does is tell the CAS login service that a single sign on
login is unacceptable. Instead, the user will need to re-enter their
username and password in order to gain access to the service.</para>
<para>The following beans should be configured to commence the CAS
authentication process:</para>
<para><programlisting>&lt;bean id="casProcessingFilter" class="net.sf.acegisecurity.ui.cas.CasProcessingFilter"&gt;
&lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
&lt;property name="authenticationFailureUrl"&gt;&lt;value&gt;/casfailed.jsp&lt;/value&gt;&lt;/property&gt;
&lt;property name="defaultTargetUrl"&gt;&lt;value&gt;/&lt;/value&gt;&lt;/property&gt;
&lt;property name="filterProcessesUrl"&gt;&lt;value&gt;/j_acegi_cas_security_check&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;
&lt;property name="filterSecurityInterceptor"&gt;&lt;ref bean="filterInvocationInterceptor"/&gt;&lt;/property&gt;
&lt;property name="authenticationEntryPoint"&gt;&lt;ref bean="casProcessingFilterEntryPoint"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint"&gt;
&lt;property name="loginUrl"&gt;&lt;value&gt;https://localhost:8443/cas/login&lt;/value&gt;&lt;/property&gt;
&lt;property name="serviceProperties"&gt;&lt;ref bean="serviceProperties"/&gt;&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>You will also need to add the
<literal>CasProcessingFilter</literal> to web.xml:</para>
<para><programlisting>&lt;filter&gt;
&lt;filter-name&gt;Acegi CAS Processing Filter&lt;/filter-name&gt;
&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;targetClass&lt;/param-name&gt;
&lt;param-value&gt;net.sf.acegisecurity.ui.cas.CasProcessingFilter&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;Acegi CAS Processing Filter&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</programlisting></para>
<para>The <literal>CasProcessingFilter</literal> has very similar
properties to the <literal>AuthenticationProcessingFilter</literal>
(used for form-based logins). Each property is
self-explanatory.</para>
<para>For CAS to operate, the
<literal>SecurityEnforcementFilter</literal> must have its
<literal>authenticationEntryPoint</literal> property set to the
<literal>CasProcessingFilterEntryPoint</literal> bean. </para>
<para>The <literal>CasProcessingFilterEntryPoint</literal> must refer
to the <literal>ServiceProperties</literal> bean (discussed above) and
provide the URL to the enterprise's CAS login server. This is where
the user's browser will be redirected.</para>
<para>Next you need to add an <literal>AuthenticationManager</literal>
that uses <literal>CasAuthenticationProvider</literal> and its
collaborators:</para>
<para><programlisting>&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;
&lt;property name="providers"&gt;
&lt;list&gt;
&lt;ref bean="casAuthenticationProvider"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casAuthenticationProvider" class="net.sf.acegisecurity.providers.cas.CasAuthenticationProvider"&gt;
&lt;property name="casAuthoritiesPopulator"&gt;&lt;ref bean="casAuthoritiesPopulator"/&gt;&lt;/property&gt;
&lt;property name="casProxyDecider"&gt;&lt;ref bean="casProxyDecider"/&gt;&lt;/property&gt;
&lt;property name="ticketValidator"&gt;&lt;ref bean="casProxyTicketValidator"/&gt;&lt;/property&gt;
&lt;property name="statelessTicketCache"&gt;&lt;ref bean="statelessTicketCache"/&gt;&lt;/property&gt;
&lt;property name="key"&gt;&lt;value&gt;my_password_for_this_auth_provider_only&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casProxyTicketValidator" class="net.sf.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator"&gt;
&lt;property name="casValidate"&gt;&lt;value&gt;https://localhost:8443/cas/proxyValidate&lt;/value&gt;&lt;/property&gt;
&lt;property name="proxyCallbackUrl"&gt;&lt;value&gt;https://localhost:8443/contacts-cas/casProxy/receptor&lt;/value&gt;&lt;/property&gt;
&lt;property name="serviceProperties"&gt;&lt;ref bean="serviceProperties"/&gt;&lt;/property&gt;
&lt;!-- &lt;property name="trustStore"&gt;&lt;value&gt;/some/path/to/your/lib/security/cacerts&lt;/value&gt;&lt;/property&gt; --&gt;
&lt;/bean&gt;
&lt;bean id="statelessTicketCache" class="net.sf.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache"&gt;
&lt;property name="minutesToIdle"&gt;&lt;value&gt;20&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator"&gt;
&lt;property name="authenticationDao"&gt;&lt;ref bean="inMemoryDaoImpl"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casProxyDecider" class="net.sf.acegisecurity.providers.cas.proxy.RejectProxyTickets"/&gt;</programlisting></para>
<para>The beans are all reasonable self-explanatory if you refer back
to the "How CAS Works" section. Careful readers might notice one
surprise: the <literal>statelessTicketCache</literal> property of the
<literal>CasAuthenticationProvider</literal>. This is discussed in
detail in the "Advanced CAS Usage" section.</para>
<para>Note the <literal>CasProxyTicketValidator</literal> has a
remarked out <literal>trustStore</literal> property. This property
might be helpful if you experience HTTPS certificate issues. Also note
the <literal>proxyCallbackUrl</literal> is set so the service can
receive a proxy-granting ticket. As mentioned above, this is optional
and unnecessary if you do not require proxy-granting tickets. If you
do use this feature, you will need to configure a suitable servlet to
receive the proxy-granting tickets. We suggest you use CAS'
<literal>ProxyTicketReceptor</literal> by adding the following to your
web application's <literal>web.xml</literal>:</para>
<para><programlisting>&lt;servlet&gt;
&lt;servlet-name&gt;casproxy&lt;/servlet-name&gt;
&lt;servlet-class&gt;edu.yale.its.tp.cas.proxy.ProxyTicketReceptor&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;casproxy&lt;/servlet-name&gt;
&lt;url-pattern&gt;/casProxy/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</programlisting></para>
<para>This completes the configuration of CAS. If you haven't made any
mistakes, your web application should happily work within the
framework of CAS single sign on. No other parts of the Acegi Security
System for Spring need to be concerned about the fact CAS handled
authentication.</para>
<para>There is also a <literal>contacts-cas.war</literal> file in the
sample applications directory. This sample application uses the above
settings and can be deployed to see CAS in operation.</para>
</sect2>
<sect2 id="security-cas-advanced-usage">
<title>Advanced CAS Usage</title>
<para>[DRAFT - COMMENTS WELCOME]</para>
<para>The <literal>CasAuthenticationProvider</literal> distinguishes
between stateful and stateless clients. A stateful client is
considered any that originates via the
<literal>CasProcessingFilter</literal>. A stateless client is any that
presents an authentication request via the
<literal>UsernamePasswordAuthenticationToken</literal> with a
principal equal to
<literal>CasProcessingFilter.CAS_STATELESS_IDENTIFIER</literal>.</para>
<para>Stateless clients are likely to be via remoting protocols such
as Hessian and Burlap. The <literal>BasicProcessingFilter</literal> is
still used in this case, but the remoting protocol client is expected
to present a username equal to the static string above, and a password
equal to a CAS service ticket. Clients should acquire a CAS service
ticket directly from the CAS server.</para>
<para>Because remoting protocols have no way of presenting themselves
within the context of a <literal>HttpSession</literal>, it isn't
possible to rely on the <literal>HttpSession</literal>'s
<literal>HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY</literal>
attribute to locate the CasAuthenticationToken. Furthermore, because
the CAS server invalidates a service ticket after it has been
validated by the TicketValidator, presenting the same service ticket
on subsequent requests will not work. It is similarly very difficult
to obtain a proxy-granting ticket for a remoting protocol client, as
they are often operational on client machines which do not have HTTPS
certificates that would be trusted by the CAS server.</para>
<para>One obvious option is to not use CAS at all for remoting
protocol clients. However, this would eliminate many of the desirable
features of CAS.</para>
<para>As a middle-ground, the CasAuthenticationProvider uses a
StatelessTicketCache. This is used solely for requests with a
principal equal to
<literal>CasProcessingFilter.CAS_STATELESS_IDENTIFIER</literal>. What
happens is the CasAuthenticationProvider will store the resulting
CasAuthenticationToken in the StatelessTicketCache, keyed on the
service ticket. Accordingly, remoting protocol clients can present the
same service ticket and the CasAuthenticationProvider will not need to
contact the CAS server for validation.</para>
<para>The other aspect of advanced CAS usage involves creating proxy
tickets from the proxy-granting ticket. As indicated above, we
recommend you use CAS' <literal>ProxyTicketReceptor</literal> to
receive these tickets. The <literal>ProxyTicketReceptor</literal>
provides a static method that enables you to obtain a proxy ticket by
presenting the proxy-granting IOU ticket. You can obtain the
proxy-granting IOU ticket by calling
<literal>CasAuthenticationToken.getProxyGrantingTicketIou()</literal>.</para>
<para>It is hoped you find CAS integration easy and useful with the
Acegi Security System for Spring classes. Welcome to enterprise-wide
single sign on!</para>
</sect2>
</sect1>
<sect1 id="security-sample">
<title>Contacts Sample Application</title>

29
lib/cas/LICENSE Normal file
View File

@ -0,0 +1,29 @@
Copyright (c) 2000-2003 Yale University. All rights reserved.
THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY
DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH
DAMAGE.
Redistribution and use of this software in source or binary forms,
with or without modification, are permitted, provided that the
following conditions are met:
1. Any redistribution must include the above copyright notice and
disclaimer and this list of conditions in any related documentation
and, if feasible, in the redistributed software.
2. Any redistribution must include the acknowledgment, "This product
includes software developed by Yale University," in any related
documentation and, if feasible, in the redistributed software.
3. The names "Yale" and "Yale University" must not be used to endorse
or promote products derived from this software.

2
lib/cas/version.txt Normal file
View File

@ -0,0 +1,2 @@
CAS Server 2.0.12 beta 3
CAS Client 2.0.10

58
lib/ehcache/LICENSE.txt Normal file
View File

@ -0,0 +1,58 @@
/*
* $Id$
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/

1
lib/ehcache/version.txt Normal file
View File

@ -0,0 +1 @@
EHCACHE 0.7

View File

@ -156,6 +156,58 @@
<lib dir="${dist.lib.dir}" includes="acegi-security.jar"/>
</war>
<!-- Make WAR suitable for deployment into containers using Yale CAS -->
<delete file="${dist.dir}/${name.cas}.war"/>
<mkdir dir="${tmp.dir}/${name.cas}"/>
<copy todir="${tmp.dir}/${name.cas}">
<fileset dir="${war.dir}">
<include name="**"/>
</fileset>
<fileset dir="${etc.dir}/cas">
<include name="casfailed.jsp"/>
</fileset>
</copy>
<copy todir="${tmp.dir}/${name.cas}/classes">
<fileset dir="${build.dir}">
<include name="**"/>
</fileset>
</copy>
<copy todir="${tmp.dir}/${name.cas}/WEB-INF">
<fileset dir="${etc.dir}/cas">
<include name="web.xml"/>
<include name="applicationContext.xml"/>
</fileset>
</copy>
<war warfile="${dist.dir}/${name.cas}.war" webxml="${tmp.dir}/${name.cas}/WEB-INF/web.xml">
<!-- Include the files which are not under WEB-INF -->
<fileset dir="${tmp.dir}/${name.cas}">
<exclude name="WEB-INF/**"/>
<exclude name="classes/**"/>
</fileset>
<!-- Bring in various XML configuration files -->
<webinf dir="${tmp.dir}/${name.cas}/WEB-INF">
<exclude name="web.xml"/>
</webinf>
<!-- Include the compiled classes -->
<classes dir="${tmp.dir}/${name.cas}/classes"/>
<!-- Include required libraries -->
<lib dir="${lib.dir}/jakarta-taglibs" includes="*.jar"/>
<lib dir="${lib.dir}/spring" includes="spring.jar"/>
<lib dir="${lib.dir}/aop-alliance" includes="aopalliance.jar"/>
<lib dir="${lib.dir}/regexp" includes="jakarta-oro.jar"/>
<lib dir="${lib.dir}/j2ee" includes="jstl.jar"/>
<lib dir="${lib.dir}/caucho" includes="*.jar"/>
<lib dir="${lib.dir}/jakarta-commons" includes="commons-codec.jar"/>
<lib dir="${lib.dir}/jakarta-commons" includes="commons-collections.jar"/>
<lib dir="${lib.dir}/cas" includes="*.jar"/>
<lib dir="${lib.dir}/ehcache" includes="ehcache.jar"/>
<lib dir="${dist.lib.dir}" includes="acegi-security-taglib.jar"/>
<lib dir="${dist.lib.dir}" includes="acegi-security.jar"/>
</war>
<!-- Make WAR suitable for deployment into containers WITH container adapters -->
<delete file="${dist.dir}/${name.ca}.war"/>
<mkdir dir="${tmp.dir}/${name.ca}"/>

View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
- Application context loaded by ContextLoaderListener if NOT using container adapters
- $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- RunAsManager -->
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<bean id="runAsAuthenticationProvider" class="net.sf.acegisecurity.runas.RunAsImplAuthenticationProvider">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="runAsAuthenticationProvider"/>
<ref bean="casAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=PASSWORD_NOT_USED,ROLE_TELLER,ROLE_SUPERVISOR
dianne=PASSWORD_NOT_USED,ROLE_TELLER
scott=PASSWORD_NOT_USED,ROLE_TELLER
peter=PASSWORD_NOT_USED_AND_DISABLED_IGNORED,disabled,ROLE_TELLER
</value>
</property>
</bean>
<bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="authenticationEntryPoint"><ref bean="basicProcessingFilterEntryPoint"/></property>
</bean>
<bean id="basicProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName"><value>Contacts Realm</value></property>
</bean>
<bean id="casAuthenticationProvider" class="net.sf.acegisecurity.providers.cas.CasAuthenticationProvider">
<property name="casAuthoritiesPopulator"><ref bean="casAuthoritiesPopulator"/></property>
<property name="casProxyDecider"><ref bean="casProxyDecider"/></property>
<property name="ticketValidator"><ref bean="casProxyTicketValidator"/></property>
<property name="statelessTicketCache"><ref bean="statelessTicketCache"/></property>
<property name="key"><value>my_password_for_this_auth_provider_only</value></property>
</bean>
<bean id="casProxyTicketValidator" class="net.sf.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
<property name="casValidate"><value>https://localhost:8443/cas/proxyValidate</value></property>
<property name="proxyCallbackUrl"><value>https://localhost:8443/contacts-cas/casProxy/receptor</value></property>
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
<!-- <property name="trustStore"><value>/some/path/to/your/lib/security/cacerts</value></property> -->
</bean>
<bean id="statelessTicketCache" class="net.sf.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
<property name="minutesToIdle"><value>20</value></property>
</bean>
<bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
</bean>
<bean id="casProxyDecider" class="net.sf.acegisecurity.providers.cas.proxy.RejectProxyTickets">
</bean>
<bean id="serviceProperties" class="net.sf.acegisecurity.ui.cas.ServiceProperties">
<property name="service"><value>https://localhost:8443/contacts-cas/j_acegi_cas_security_check</value></property>
<property name="sendRenew"><value>false</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
<!-- An access decision voter that reads ROLE_* configuaration settings -->
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<!-- An access decision voter that reads CONTACT_OWNED_BY_CURRENT_USER configuaration settings -->
<bean id="contactSecurityVoter" class="sample.contact.ContactSecurityVoter"/>
<!-- An access decision manager used by the business objects -->
<bean id="businessAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<ref bean="contactSecurityVoter"/>
</list>
</property>
</bean>
<!-- ===================== SECURITY DEFINITIONS ======================= -->
<bean id="publicContactManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="businessAccessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.delete=ROLE_SUPERVISOR,RUN_AS_SERVER
sample.contact.ContactManager.getAllByOwner=CONTACT_OWNED_BY_CURRENT_USER,RUN_AS_SERVER
sample.contact.ContactManager.save=CONTACT_OWNED_BY_CURRENT_USER,RUN_AS_SERVER
sample.contact.ContactManager.getById=ROLE_TELLER,RUN_AS_SERVER
</value>
</property>
</bean>
<!-- We expect all callers of the backend object to hold the role ROLE_RUN_AS_SERVER -->
<bean id="backendContactManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="businessAccessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.delete=ROLE_RUN_AS_SERVER
sample.contact.ContactManager.getAllByOwner=ROLE_RUN_AS_SERVER
sample.contact.ContactManager.save=ROLE_RUN_AS_SERVER
sample.contact.ContactManager.getById=ROLE_RUN_AS_SERVER
</value>
</property>
</bean>
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
<bean id="contactManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>sample.contact.ContactManager</value></property>
<property name="interceptorNames">
<list>
<value>publicContactManagerSecurity</value>
<value>publicContactManagerTarget</value>
</list>
</property>
</bean>
<bean id="publicContactManagerTarget" class="sample.contact.ContactManagerFacade">
<property name="backend"><ref bean="backendContactManager"/></property>
</bean>
<bean id="backendContactManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>sample.contact.ContactManager</value></property>
<property name="interceptorNames">
<list>
<value>backendContactManagerSecurity</value>
<value>backendContactManagerTarget</value>
</list>
</property>
</bean>
<bean id="backendContactManagerTarget" class="sample.contact.ContactManagerBackend"/>
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
<bean id="casProcessingFilter" class="net.sf.acegisecurity.ui.cas.CasProcessingFilter">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="authenticationFailureUrl"><value>/casfailed.jsp</value></property>
<property name="defaultTargetUrl"><value>/</value></property>
<property name="filterProcessesUrl"><value>/j_acegi_cas_security_check</value></property>
</bean>
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
</bean>
<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
<property name="loginUrl"><value>https://localhost:8443/cas/login</value></property>
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
</bean>
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<!-- Note the order that entries are placed against the objectDefinitionSource is critical.
The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last -->
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="httpRequestAccessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/secure/super.*\Z=ROLE_WE_DONT_HAVE
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
</value>
</property>
</bean>
<!-- BASIC Regular Expression Syntax (for beginners):
\A means the start of the string (ie the beginning of the URL)
\Z means the end of the string (ie the end of the URL)
. means any single character
* means null or any number of repetitions of the last expression (so .* means zero or more characters)
Some examples:
Expression: \A/my/directory/.*\Z
Would match: /my/directory/
/my/directory/hello.html
Expression: \A/.*\Z
Would match: /hello.html
/
Expression: \A/.*/secret.html\Z
Would match: /some/directory/secret.html
/another/secret.html
Not match: /anothersecret.html (missing required /)
-->
</beans>

View File

@ -0,0 +1,20 @@
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
<%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %>
<%@ page import="net.sf.acegisecurity.AuthenticationException" %>
<%-- This page will be copied into WAR's root directory if using CAS --%>
<html>
<head>
<title>Login to CAS failed!</title>
</head>
<body>
<h1>Login to CAS failed!</h1>
<font color="red">
Your CAS credentials were rejected.<BR><BR>
Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
</font>
</body>
</html>

View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
<!--
- Contacts web application
- $Id$
- File will be copied into WAR's WEB-INF directory if NOT using container adapter
-->
<web-app>
<display-name>Contacts Sample Application</display-name>
<description>
Example of an application secured using Acegi Security System for Spring.
</description>
<!-- Required for CAS ProxyTicketReceptor servlet. This is the
URL to CAS' "proxy" actuator, where a PGT and TargetService can
be presented to obtain a new proxy ticket. THIS CAN BE
REMOVED IF THE APPLICATION DOESN'T NEED TO ACT AS A PROXY -->
<context-param>
<param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
<param-value>http://localhost:8433/cas/proxy</param-value>
</context-param>
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>Acegi CAS Processing Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.ui.cas.CasProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
<filter-class>net.sf.acegisecurity.ui.AutoIntegrationFilter</filter-class>
</filter>
<filter>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi CAS Processing Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
- Loads the root application context of this web app at startup,
- by default from "/WEB-INF/applicationContext.xml".
- Use WebApplicationContextUtils.getWebApplicationContext(servletContext)
- to access it anywhere in the web application, outside of the framework.
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
- Servlet that dispatches request to registered handlers (Controller implementations).
- Has its own application context, by default defined in "{servlet-name}-servlet.xml",
- i.e. "contacts-servlet.xml".
-
- A web app can contain any number of such servlets.
- Note that this web app does not have a shared root application context,
- therefore the DispatcherServlet contexts do not have a common parent.
-->
<servlet>
<servlet-name>contacts</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>caucho</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- CAS servlet which receives a proxy-granting ticket from the CAS
server. THIS CAN BE REMOVED IF THE APPLICATION DOESN'T NEED TO
ACT AS A PROXY -->
<servlet>
<servlet-name>casproxy</servlet-name>
<servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<!--
- Maps the contacts dispatcher to /*.
-
-->
<servlet-mapping>
<servlet-name>contacts</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<!--
- Dispatcher servlet mapping for HTTP remoting via the Caucho protocols,
- i.e. Hessian and Burlap (see caucho-servlet.xml for the controllers).
-->
<servlet-mapping>
<servlet-name>caucho</servlet-name>
<url-pattern>/caucho/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>casproxy</servlet-name>
<url-pattern>/casProxy/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>/spring</taglib-uri>
<taglib-location>/WEB-INF/spring.tld</taglib-location>
</taglib>
</web-app>

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIIC1DCCAj0CBECA2vQwDQYJKoZIhvcNAQEEBQAwgbAxEDAOBgNVBAYTB1Vua25vd24xEDAOBgNV
BAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24xOTA3BgNVBAoTMFRFU1QgQ0VSVElGSUNBVEUg
T05MWS4gRE8gTk9UIFVTRSBJTiBQUk9EVUNUSU9OLjEpMCcGA1UECxMgQWNlZ2kgU2VjdXJpdHkg
U3lzdGVtIGZvciBTcHJpbmcxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0wNDA0MTcwNzIxMjRaFw0z
MTA5MDIwNzIxMjRaMIGwMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYD
VQQHEwdVbmtub3duMTkwNwYDVQQKEzBURVNUIENFUlRJRklDQVRFIE9OTFkuIERPIE5PVCBVU0Ug
SU4gUFJPRFVDVElPTi4xKTAnBgNVBAsTIEFjZWdpIFNlY3VyaXR5IFN5c3RlbSBmb3IgU3ByaW5n
MRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANwJCiDthTNC
SJZ87CYkhWWDBciaFRvuDldzgEGwEUF5gNczd8Er66Pvh+Ir350hjE4LsDfi5iQNOuhbRR37LvW5
7CrKG3W+vq7K3Zr9JEqhP/U2ocPLQQF4/NbBKStRacGGY1O3koTqp9W8gE0vSlC3/KhoOoPWHkGh
NZXOxuwLAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAdTlsziREdIR00/+tufCq/ACHSo2nJr1yRzIi
cVOXJBws0f+M3TUSIaODFv/54bZnWtjlWGa55uhc425+LrkOtqus7cMoNnyte/C6g/gcnArkKVhL
C68LGqARe9DK1ycquA4KmgiKyhi9a54kKA6BC4bmmEI98HpB6uxxvOB+ChE=
-----END CERTIFICATE-----

View File

@ -0,0 +1,82 @@
$Id$
CAS requires HTTPS be used for all operations, with the certificate used
having been signed by a certificate in the cacerts files shipped with Java.
If you're using a HTTPS certificate signed by a well known authority
(like Verisign), you can safely ignore the procedure below (although you
might find the troubleshooting section at the end helpful).
The following demonstrates how to create a self-signed certificate and add
it to the cacerts file. If you just want to use the certificate we have
already created and shipped with the Acegi Security System for Spring, you
can skip directly to step 3.
1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: Acegi Security System for Spring
What is the name of your organization?
[Unknown]: TEST CERTIFICATE ONLY. DO NOT USE IN PRODUCTION.
What is the name of your City or Locality?
[Unknown]:
What is the name of your State or Province?
[Unknown]:
What is the two-letter country code for this unit?
[Unknown]:
Is CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ONLY. D
O NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown correct?
[no]: yes
2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password
3. copy acegisecurity.txt %JAVA_HOME%\lib\security
4. copy keystore %YOUR_WEB_CONTAINER_LOCATION%
NOTE: You will need to configure your web container as appropriate.
We recommend you test the certificate works by visiting
https://localhost:8443. When prompted by your browser, select to
install the certificate.
5. cd %JAVA_HOME%\lib\security
6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity
Owner: CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ONL
Y. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
Issuer: CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ON
LY. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
Serial number: 4080daf4
Valid from: Sat Apr 17 07:21:24 GMT 2004 until: Tue Sep 02 07:21:24 GMT 2031
Certificate fingerprints:
MD5: B4:AC:A8:24:34:99:F1:A9:F8:1D:A5:6C:BF:0A:34:FA
SHA1: F1:E6:B1:3A:01:39:2D:CF:06:FA:82:AB:86:0D:77:9D:06:93:D6:B0
Trust this certificate? [no]: yes
Certificate was added to keystore
[Saving cacerts]
7. Finished. You can now run the sample application as if you purchased a
properly signed certificate. For production applications, of course you should
use an appropriately signed certificate so your web visitors will trust it
(such as issued by Thawte, Verisign etc).
TROUBLESHOOTING
* A "sun.security.validator.ValidatorException: No trusted certificate
found" indicates the cacerts is not being used or it did not correctly
import the certificate. To rule out your web container replacing or in
some way modifying the trust manager, set the
CasAuthenticationProvider.trustStore property to the full file system
location to your cacerts file.
* If your web container is ignoring your cacerts file, double-check it
is stored in $JAVA_HOME\lib\security\cacerts. $JAVA_HOME might be
pointing to the SDK, not JRE. In that case, copy
$JAVA_HOME\jre\lib\security\cacerts to $JAVA_HOME\lib\security\cacerts

Binary file not shown.

View File

@ -3,6 +3,7 @@
name.filter=contacts
name.ca=contacts-container-adapter
name.cas=contacts-cas
src.dir=src
war.dir=war
lib.dir=${basedir}/../../lib