Initial CAS support.
This commit is contained in:
parent
782bfe5a74
commit
fa9b872570
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
Adapter to Yale Central Authentication Service (CAS).
|
||||
<p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
108
core/src/main/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCache.java
vendored
Normal file
108
core/src/main/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCache.java
vendored
Normal 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
An authentication provider that can process Yale Central Authentication Service (CAS)
|
||||
service tickets and proxy tickets.
|
||||
</body>
|
||||
</html>
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Implementations that populate GrantedAuthority[]s of CAS authentications.
|
||||
</body>
|
||||
</html>
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
Implementations that decide whether proxy lists of
|
||||
CAS authentications are trusted.
|
||||
</body>
|
||||
</html>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Implementations that validate service tickets.
|
||||
</body>
|
||||
</html>
|
|
@ -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 {}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
Authenticates standard web browser users via
|
||||
Yale Central Authentication Service (CAS).
|
||||
</body>
|
||||
</html>
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
89
core/src/test/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCacheTests.java
vendored
Normal file
89
core/src/test/java/org/acegisecurity/providers/cas/cache/EhCacheBasedTicketCacheTests.java
vendored
Normal 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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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><bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||
<property name="saltSource"><ref bean="saltSource"/></property>
|
||||
<property name="passwordEncoder"><ref bean="passwordEncoder"/></property>
|
||||
</bean></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&ticket=ST-0-ER94xMJmn6pha35CQRoZ&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&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><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></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><context-param>
|
||||
<param-name>edu.yale.its.tp.cas.authHandler</param-name>
|
||||
<param-value>net.sf.acegisecurity.adapters.cas.CasPasswordHandlerProxy</param-value>
|
||||
</context-param>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/applicationContext.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener></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><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></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><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></programlisting></para>
|
||||
|
||||
<para>You will also need to add the
|
||||
<literal>CasProcessingFilter</literal> to web.xml:</para>
|
||||
|
||||
<para><programlisting><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-mapping>
|
||||
<filter-name>Acegi CAS Processing Filter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping></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><bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
|
||||
<property name="providers">
|
||||
<list>
|
||||
<ref bean="casAuthenticationProvider"/>
|
||||
</list>
|
||||
</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"/></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><servlet>
|
||||
<servlet-name>casproxy</servlet-name>
|
||||
<servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>casproxy</servlet-name>
|
||||
<url-pattern>/casProxy/*</url-pattern>
|
||||
</servlet-mapping></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>
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -0,0 +1,2 @@
|
|||
CAS Server 2.0.12 beta 3
|
||||
CAS Client 2.0.10
|
|
@ -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/>.
|
||||
*
|
||||
*/
|
|
@ -0,0 +1 @@
|
|||
EHCACHE 0.7
|
|
@ -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}"/>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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-----
|
|
@ -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.
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue