mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-09 11:53:30 +00:00
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/regexp/jakarta-oro.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/jakarta-commons/commons-codec.jar"/>
|
<classpathentry kind="lib" path="lib/jakarta-commons/commons-codec.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/hsqldb/hsqldb.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"/>
|
<classpathentry kind="output" path="target/eclipseclasses"/>
|
||||||
</classpath>
|
</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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
core/src/main/java/org/acegisecurity/ui/cas/package.html
Normal file
6
core/src/main/java/org/acegisecurity/ui/cas/package.html
Normal file
@ -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;
|
package net.sf.acegisecurity.providers.dao;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import net.sf.acegisecurity.GrantedAuthority;
|
import net.sf.acegisecurity.GrantedAuthority;
|
||||||
|
|
||||||
|
|
||||||
@ -24,7 +26,7 @@ import net.sf.acegisecurity.GrantedAuthority;
|
|||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class User {
|
public class User implements Serializable {
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
private String password;
|
private String password;
|
||||||
@ -53,9 +55,10 @@ public class User {
|
|||||||
*/
|
*/
|
||||||
public User(String username, String password, boolean enabled,
|
public User(String username, String password, boolean enabled,
|
||||||
GrantedAuthority[] authorities) throws IllegalArgumentException {
|
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(
|
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++) {
|
for (int i = 0; i < authorities.length; i++) {
|
||||||
|
@ -47,7 +47,7 @@ import javax.servlet.http.HttpSession;
|
|||||||
public class MockHttpServletRequest implements HttpServletRequest {
|
public class MockHttpServletRequest implements HttpServletRequest {
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
private HttpSession session;
|
private HttpSession session = new MockHttpSession();
|
||||||
private Map headersMap = new HashMap();
|
private Map headersMap = new HashMap();
|
||||||
private Map paramMap = new HashMap();
|
private Map paramMap = new HashMap();
|
||||||
private Principal principal;
|
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
|
handling each request. Handling involves a number of
|
||||||
operations:</para>
|
operations:</para>
|
||||||
|
|
||||||
<itemizedlist spacing="compact">
|
<orderedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>Store the configuration attributes that are associated with
|
<para>Store the configuration attributes that are associated with
|
||||||
each secure request.</para>
|
each secure request.</para>
|
||||||
@ -354,7 +354,7 @@
|
|||||||
<para>Return any result received from the
|
<para>Return any result received from the
|
||||||
<literal>SecurityInterceptorCallback</literal>.</para>
|
<literal>SecurityInterceptorCallback</literal>.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</orderedlist>
|
||||||
|
|
||||||
<para>Whilst this may seem quite involved, don't worry. Developers
|
<para>Whilst this may seem quite involved, don't worry. Developers
|
||||||
interact with the security process by simply implementing basic
|
interact with the security process by simply implementing basic
|
||||||
@ -854,6 +854,13 @@
|
|||||||
<literal>AuthenticationProvider</literal> if you were not using
|
<literal>AuthenticationProvider</literal> if you were not using
|
||||||
container adapters.</para>
|
container adapters.</para>
|
||||||
</listitem>
|
</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>
|
</itemizedlist></para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
@ -870,8 +877,26 @@
|
|||||||
|
|
||||||
<para><programlisting><bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
<para><programlisting><bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||||
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||||
|
<property name="saltSource"><ref bean="saltSource"/></property>
|
||||||
|
<property name="passwordEncoder"><ref bean="passwordEncoder"/></property>
|
||||||
</bean></programlisting></para>
|
</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
|
<para>For a class to be able to provide the
|
||||||
<literal>DaoAuthenticationProvider</literal> with access to an
|
<literal>DaoAuthenticationProvider</literal> with access to an
|
||||||
authentication repository, it must implement the
|
authentication repository, it must implement the
|
||||||
@ -957,10 +982,13 @@
|
|||||||
<para>You can use different relational database management systems by
|
<para>You can use different relational database management systems by
|
||||||
modifying the <literal>DriverManagerDataSource</literal> shown above.
|
modifying the <literal>DriverManagerDataSource</literal> shown above.
|
||||||
Irrespective of the database used, a standard schema must be used as
|
Irrespective of the database used, a standard schema must be used as
|
||||||
indicated in <literal>dbinit.txt</literal>. Of particular note is the
|
indicated in <literal>dbinit.txt</literal>.</para>
|
||||||
database must return responses that treat the username as case
|
|
||||||
insensitive, in order to comply with the
|
<para>If you default schema is unsuitable for your needs,
|
||||||
<literal>AuthenticationDao</literal> contract.</para>
|
<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
|
<para>The Acegi Security System for Spring ships with a Hypersonic SQL
|
||||||
instance that has the required authentication information and sample
|
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
|
<literal>HttpSession</literal> object and filters to authenticate the
|
||||||
user. Another approach is HTTP Basic Authentication, which allows
|
user. Another approach is HTTP Basic Authentication, which allows
|
||||||
clients to use HTTP headers to present authentication information to
|
clients to use HTTP headers to present authentication information to
|
||||||
the Acegi Security System for Spring. The final approach is via
|
the Acegi Security System for Spring. Alternatively, you can also use
|
||||||
Container Adapters, which allow supported web containers to perform
|
Yale Central Authentication Service (CAS) for enterprise-wide single
|
||||||
the authentication themselves. HTTP Session Authentication is
|
sign on. The final approach is via Container Adapters, which allow
|
||||||
discussed below, whilst Container Adapters are discussed in a separate
|
supported web containers to perform the authentication themselves.
|
||||||
section.</para>
|
HTTP Session and Basic Authentication is discussed below, whilst CAS
|
||||||
|
and Container Adapters are discussed in separate sections of this
|
||||||
|
document.</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="security-ui-http-session">
|
<sect2 id="security-ui-http-session">
|
||||||
@ -1538,7 +1568,7 @@ public boolean supports(Class clazz);</programlisting></para>
|
|||||||
<literal>authenticationFailureUrl</literal>. The
|
<literal>authenticationFailureUrl</literal>. The
|
||||||
<literal>AuthenticationException</literal> will be placed into the
|
<literal>AuthenticationException</literal> will be placed into the
|
||||||
<literal>HttpSession</literal> attribute indicated by
|
<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>
|
enabling a reason to be provided to the user on the error page.</para>
|
||||||
|
|
||||||
<para>If authentication is successful, the resulting
|
<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
|
browser will need to be redirected to the target URL. The target URL
|
||||||
is usually indicated by the <literal>HttpSession</literal> attribute
|
is usually indicated by the <literal>HttpSession</literal> attribute
|
||||||
specified by
|
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
|
This attribute is automatically set by the
|
||||||
<literal>SecurityEnforcementFilter</literal> when an
|
<literal>SecurityEnforcementFilter</literal> when an
|
||||||
<literal>AuthenticationException</literal> occurs, so that after login
|
<literal>AuthenticationException</literal> occurs, so that after login
|
||||||
@ -2100,6 +2130,569 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|||||||
</sect2>
|
</sect2>
|
||||||
</sect1>
|
</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">
|
<sect1 id="security-sample">
|
||||||
<title>Contacts Sample Application</title>
|
<title>Contacts Sample Application</title>
|
||||||
|
|
||||||
|
29
lib/cas/LICENSE
Normal file
29
lib/cas/LICENSE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Copyright (c) 2000-2003 Yale University. All rights reserved.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
Redistribution and use of this software in source or binary forms,
|
||||||
|
with or without modification, are permitted, provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
1. Any redistribution must include the above copyright notice and
|
||||||
|
disclaimer and this list of conditions in any related documentation
|
||||||
|
and, if feasible, in the redistributed software.
|
||||||
|
|
||||||
|
2. Any redistribution must include the acknowledgment, "This product
|
||||||
|
includes software developed by Yale University," in any related
|
||||||
|
documentation and, if feasible, in the redistributed software.
|
||||||
|
|
||||||
|
3. The names "Yale" and "Yale University" must not be used to endorse
|
||||||
|
or promote products derived from this software.
|
2
lib/cas/version.txt
Normal file
2
lib/cas/version.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
CAS Server 2.0.12 beta 3
|
||||||
|
CAS Client 2.0.10
|
58
lib/ehcache/LICENSE.txt
Normal file
58
lib/ehcache/LICENSE.txt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* The Apache Software License, Version 1.1
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
|
||||||
|
* reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* 3. The end-user documentation included with the redistribution, if
|
||||||
|
* any, must include the following acknowlegement:
|
||||||
|
* "This product includes software developed by the
|
||||||
|
* Apache Software Foundation (http://www.apache.org/)."
|
||||||
|
* Alternately, this acknowlegement may appear in the software itself,
|
||||||
|
* if and wherever such third-party acknowlegements normally appear.
|
||||||
|
*
|
||||||
|
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
|
||||||
|
* Foundation" must not be used to endorse or promote products derived
|
||||||
|
* from this software without prior written permission. For written
|
||||||
|
* permission, please contact apache@apache.org.
|
||||||
|
*
|
||||||
|
* 5. Products derived from this software may not be called "Apache"
|
||||||
|
* nor may "Apache" appear in their names without prior written
|
||||||
|
* permission of the Apache Group.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||||
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||||
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
1
lib/ehcache/version.txt
Normal file
1
lib/ehcache/version.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
EHCACHE 0.7
|
@ -156,6 +156,58 @@
|
|||||||
<lib dir="${dist.lib.dir}" includes="acegi-security.jar"/>
|
<lib dir="${dist.lib.dir}" includes="acegi-security.jar"/>
|
||||||
</war>
|
</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 -->
|
<!-- Make WAR suitable for deployment into containers WITH container adapters -->
|
||||||
<delete file="${dist.dir}/${name.ca}.war"/>
|
<delete file="${dist.dir}/${name.ca}.war"/>
|
||||||
<mkdir dir="${tmp.dir}/${name.ca}"/>
|
<mkdir dir="${tmp.dir}/${name.ca}"/>
|
||||||
|
229
samples/contacts/etc/cas/applicationContext.xml
Normal file
229
samples/contacts/etc/cas/applicationContext.xml
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Application context loaded by ContextLoaderListener if NOT using container adapters
|
||||||
|
- $Id$
|
||||||
|
-->
|
||||||
|
|
||||||
|
<beans>
|
||||||
|
|
||||||
|
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
|
||||||
|
|
||||||
|
<!-- RunAsManager -->
|
||||||
|
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
|
||||||
|
<property name="key"><value>my_run_as_password</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
|
||||||
|
|
||||||
|
<bean id="runAsAuthenticationProvider" class="net.sf.acegisecurity.runas.RunAsImplAuthenticationProvider">
|
||||||
|
<property name="key"><value>my_run_as_password</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
|
||||||
|
<property name="providers">
|
||||||
|
<list>
|
||||||
|
<ref bean="runAsAuthenticationProvider"/>
|
||||||
|
<ref bean="casAuthenticationProvider"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
|
||||||
|
<property name="userMap">
|
||||||
|
<value>
|
||||||
|
marissa=PASSWORD_NOT_USED,ROLE_TELLER,ROLE_SUPERVISOR
|
||||||
|
dianne=PASSWORD_NOT_USED,ROLE_TELLER
|
||||||
|
scott=PASSWORD_NOT_USED,ROLE_TELLER
|
||||||
|
peter=PASSWORD_NOT_USED_AND_DISABLED_IGNORED,disabled,ROLE_TELLER
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="authenticationEntryPoint"><ref bean="basicProcessingFilterEntryPoint"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="basicProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
|
||||||
|
<property name="realmName"><value>Contacts Realm</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="casAuthenticationProvider" class="net.sf.acegisecurity.providers.cas.CasAuthenticationProvider">
|
||||||
|
<property name="casAuthoritiesPopulator"><ref bean="casAuthoritiesPopulator"/></property>
|
||||||
|
<property name="casProxyDecider"><ref bean="casProxyDecider"/></property>
|
||||||
|
<property name="ticketValidator"><ref bean="casProxyTicketValidator"/></property>
|
||||||
|
<property name="statelessTicketCache"><ref bean="statelessTicketCache"/></property>
|
||||||
|
<property name="key"><value>my_password_for_this_auth_provider_only</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="casProxyTicketValidator" class="net.sf.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
|
||||||
|
<property name="casValidate"><value>https://localhost:8443/cas/proxyValidate</value></property>
|
||||||
|
<property name="proxyCallbackUrl"><value>https://localhost:8443/contacts-cas/casProxy/receptor</value></property>
|
||||||
|
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
|
||||||
|
<!-- <property name="trustStore"><value>/some/path/to/your/lib/security/cacerts</value></property> -->
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="statelessTicketCache" class="net.sf.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
|
||||||
|
<property name="minutesToIdle"><value>20</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator">
|
||||||
|
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="casProxyDecider" class="net.sf.acegisecurity.providers.cas.proxy.RejectProxyTickets">
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="serviceProperties" class="net.sf.acegisecurity.ui.cas.ServiceProperties">
|
||||||
|
<property name="service"><value>https://localhost:8443/contacts-cas/j_acegi_cas_security_check</value></property>
|
||||||
|
<property name="sendRenew"><value>false</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
|
||||||
|
|
||||||
|
<!-- An access decision voter that reads ROLE_* configuaration settings -->
|
||||||
|
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
|
||||||
|
|
||||||
|
<!-- An access decision voter that reads CONTACT_OWNED_BY_CURRENT_USER configuaration settings -->
|
||||||
|
<bean id="contactSecurityVoter" class="sample.contact.ContactSecurityVoter"/>
|
||||||
|
|
||||||
|
<!-- An access decision manager used by the business objects -->
|
||||||
|
<bean id="businessAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
||||||
|
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
|
||||||
|
<property name="decisionVoters">
|
||||||
|
<list>
|
||||||
|
<ref bean="roleVoter"/>
|
||||||
|
<ref bean="contactSecurityVoter"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- ===================== SECURITY DEFINITIONS ======================= -->
|
||||||
|
|
||||||
|
<bean id="publicContactManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="accessDecisionManager"><ref bean="businessAccessDecisionManager"/></property>
|
||||||
|
<property name="runAsManager"><ref bean="runAsManager"/></property>
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
sample.contact.ContactManager.delete=ROLE_SUPERVISOR,RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.getAllByOwner=CONTACT_OWNED_BY_CURRENT_USER,RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.save=CONTACT_OWNED_BY_CURRENT_USER,RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.getById=ROLE_TELLER,RUN_AS_SERVER
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- We expect all callers of the backend object to hold the role ROLE_RUN_AS_SERVER -->
|
||||||
|
<bean id="backendContactManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="accessDecisionManager"><ref bean="businessAccessDecisionManager"/></property>
|
||||||
|
<property name="runAsManager"><ref bean="runAsManager"/></property>
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
sample.contact.ContactManager.delete=ROLE_RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.getAllByOwner=ROLE_RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.save=ROLE_RUN_AS_SERVER
|
||||||
|
sample.contact.ContactManager.getById=ROLE_RUN_AS_SERVER
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
|
||||||
|
|
||||||
|
<bean id="contactManager" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||||
|
<property name="proxyInterfaces"><value>sample.contact.ContactManager</value></property>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<value>publicContactManagerSecurity</value>
|
||||||
|
<value>publicContactManagerTarget</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="publicContactManagerTarget" class="sample.contact.ContactManagerFacade">
|
||||||
|
<property name="backend"><ref bean="backendContactManager"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="backendContactManager" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||||
|
<property name="proxyInterfaces"><value>sample.contact.ContactManager</value></property>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<value>backendContactManagerSecurity</value>
|
||||||
|
<value>backendContactManagerTarget</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="backendContactManagerTarget" class="sample.contact.ContactManagerBackend"/>
|
||||||
|
|
||||||
|
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
|
||||||
|
|
||||||
|
<bean id="casProcessingFilter" class="net.sf.acegisecurity.ui.cas.CasProcessingFilter">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="authenticationFailureUrl"><value>/casfailed.jsp</value></property>
|
||||||
|
<property name="defaultTargetUrl"><value>/</value></property>
|
||||||
|
<property name="filterProcessesUrl"><value>/j_acegi_cas_security_check</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
||||||
|
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
||||||
|
<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
|
||||||
|
<property name="loginUrl"><value>https://localhost:8443/cas/login</value></property>
|
||||||
|
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
||||||
|
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
|
||||||
|
<property name="decisionVoters">
|
||||||
|
<list>
|
||||||
|
<ref bean="roleVoter"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- Note the order that entries are placed against the objectDefinitionSource is critical.
|
||||||
|
The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
|
||||||
|
Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last -->
|
||||||
|
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="accessDecisionManager"><ref bean="httpRequestAccessDecisionManager"/></property>
|
||||||
|
<property name="runAsManager"><ref bean="runAsManager"/></property>
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||||
|
\A/secure/super.*\Z=ROLE_WE_DONT_HAVE
|
||||||
|
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- BASIC Regular Expression Syntax (for beginners):
|
||||||
|
|
||||||
|
\A means the start of the string (ie the beginning of the URL)
|
||||||
|
\Z means the end of the string (ie the end of the URL)
|
||||||
|
. means any single character
|
||||||
|
* means null or any number of repetitions of the last expression (so .* means zero or more characters)
|
||||||
|
|
||||||
|
Some examples:
|
||||||
|
|
||||||
|
Expression: \A/my/directory/.*\Z
|
||||||
|
Would match: /my/directory/
|
||||||
|
/my/directory/hello.html
|
||||||
|
|
||||||
|
Expression: \A/.*\Z
|
||||||
|
Would match: /hello.html
|
||||||
|
/
|
||||||
|
|
||||||
|
Expression: \A/.*/secret.html\Z
|
||||||
|
Would match: /some/directory/secret.html
|
||||||
|
/another/secret.html
|
||||||
|
Not match: /anothersecret.html (missing required /)
|
||||||
|
-->
|
||||||
|
|
||||||
|
</beans>
|
20
samples/contacts/etc/cas/casfailed.jsp
Normal file
20
samples/contacts/etc/cas/casfailed.jsp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
|
||||||
|
<%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %>
|
||||||
|
<%@ page import="net.sf.acegisecurity.AuthenticationException" %>
|
||||||
|
<%-- This page will be copied into WAR's root directory if using CAS --%>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Login to CAS failed!</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Login to CAS failed!</h1>
|
||||||
|
|
||||||
|
<font color="red">
|
||||||
|
Your CAS credentials were rejected.<BR><BR>
|
||||||
|
Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
|
||||||
|
</font>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
160
samples/contacts/etc/cas/web.xml
Normal file
160
samples/contacts/etc/cas/web.xml
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Contacts web application
|
||||||
|
- $Id$
|
||||||
|
- File will be copied into WAR's WEB-INF directory if NOT using container adapter
|
||||||
|
-->
|
||||||
|
|
||||||
|
<web-app>
|
||||||
|
|
||||||
|
<display-name>Contacts Sample Application</display-name>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
Example of an application secured using Acegi Security System for Spring.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<!-- Required for CAS ProxyTicketReceptor servlet. This is the
|
||||||
|
URL to CAS' "proxy" actuator, where a PGT and TargetService can
|
||||||
|
be presented to obtain a new proxy ticket. THIS CAN BE
|
||||||
|
REMOVED IF THE APPLICATION DOESN'T NEED TO ACT AS A PROXY -->
|
||||||
|
<context-param>
|
||||||
|
<param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
|
||||||
|
<param-value>http://localhost:8433/cas/proxy</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Location of the XML file that defines the root application context
|
||||||
|
- Applied by ContextLoaderListener.
|
||||||
|
-->
|
||||||
|
<context-param>
|
||||||
|
<param-name>contextConfigLocation</param-name>
|
||||||
|
<param-value>/WEB-INF/applicationContext.xml</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Acegi CAS Processing Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.ui.cas.CasProcessingFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.ui.AutoIntegrationFilter</filter-class>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Acegi HTTP Request Security Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi CAS Processing Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi HTTP Request Security Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Loads the root application context of this web app at startup,
|
||||||
|
- by default from "/WEB-INF/applicationContext.xml".
|
||||||
|
- Use WebApplicationContextUtils.getWebApplicationContext(servletContext)
|
||||||
|
- to access it anywhere in the web application, outside of the framework.
|
||||||
|
-->
|
||||||
|
<listener>
|
||||||
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Servlet that dispatches request to registered handlers (Controller implementations).
|
||||||
|
- Has its own application context, by default defined in "{servlet-name}-servlet.xml",
|
||||||
|
- i.e. "contacts-servlet.xml".
|
||||||
|
-
|
||||||
|
- A web app can contain any number of such servlets.
|
||||||
|
- Note that this web app does not have a shared root application context,
|
||||||
|
- therefore the DispatcherServlet contexts do not have a common parent.
|
||||||
|
-->
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>contacts</servlet-name>
|
||||||
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
|
<load-on-startup>1</load-on-startup>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>caucho</servlet-name>
|
||||||
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
|
<load-on-startup>2</load-on-startup>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<!-- CAS servlet which receives a proxy-granting ticket from the CAS
|
||||||
|
server. THIS CAN BE REMOVED IF THE APPLICATION DOESN'T NEED TO
|
||||||
|
ACT AS A PROXY -->
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>casproxy</servlet-name>
|
||||||
|
<servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class>
|
||||||
|
<load-on-startup>3</load-on-startup>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Maps the contacts dispatcher to /*.
|
||||||
|
-
|
||||||
|
-->
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>contacts</servlet-name>
|
||||||
|
<url-pattern>*.htm</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
- Dispatcher servlet mapping for HTTP remoting via the Caucho protocols,
|
||||||
|
- i.e. Hessian and Burlap (see caucho-servlet.xml for the controllers).
|
||||||
|
-->
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>caucho</servlet-name>
|
||||||
|
<url-pattern>/caucho/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>casproxy</servlet-name>
|
||||||
|
<url-pattern>/casProxy/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<welcome-file-list>
|
||||||
|
<welcome-file>index.jsp</welcome-file>
|
||||||
|
</welcome-file-list>
|
||||||
|
|
||||||
|
<taglib>
|
||||||
|
<taglib-uri>/spring</taglib-uri>
|
||||||
|
<taglib-location>/WEB-INF/spring.tld</taglib-location>
|
||||||
|
</taglib>
|
||||||
|
|
||||||
|
</web-app>
|
15
samples/contacts/etc/ssl/acegisecurity.txt
Normal file
15
samples/contacts/etc/ssl/acegisecurity.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC1DCCAj0CBECA2vQwDQYJKoZIhvcNAQEEBQAwgbAxEDAOBgNVBAYTB1Vua25vd24xEDAOBgNV
|
||||||
|
BAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24xOTA3BgNVBAoTMFRFU1QgQ0VSVElGSUNBVEUg
|
||||||
|
T05MWS4gRE8gTk9UIFVTRSBJTiBQUk9EVUNUSU9OLjEpMCcGA1UECxMgQWNlZ2kgU2VjdXJpdHkg
|
||||||
|
U3lzdGVtIGZvciBTcHJpbmcxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0wNDA0MTcwNzIxMjRaFw0z
|
||||||
|
MTA5MDIwNzIxMjRaMIGwMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYD
|
||||||
|
VQQHEwdVbmtub3duMTkwNwYDVQQKEzBURVNUIENFUlRJRklDQVRFIE9OTFkuIERPIE5PVCBVU0Ug
|
||||||
|
SU4gUFJPRFVDVElPTi4xKTAnBgNVBAsTIEFjZWdpIFNlY3VyaXR5IFN5c3RlbSBmb3IgU3ByaW5n
|
||||||
|
MRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANwJCiDthTNC
|
||||||
|
SJZ87CYkhWWDBciaFRvuDldzgEGwEUF5gNczd8Er66Pvh+Ir350hjE4LsDfi5iQNOuhbRR37LvW5
|
||||||
|
7CrKG3W+vq7K3Zr9JEqhP/U2ocPLQQF4/NbBKStRacGGY1O3koTqp9W8gE0vSlC3/KhoOoPWHkGh
|
||||||
|
NZXOxuwLAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAdTlsziREdIR00/+tufCq/ACHSo2nJr1yRzIi
|
||||||
|
cVOXJBws0f+M3TUSIaODFv/54bZnWtjlWGa55uhc425+LrkOtqus7cMoNnyte/C6g/gcnArkKVhL
|
||||||
|
C68LGqARe9DK1ycquA4KmgiKyhi9a54kKA6BC4bmmEI98HpB6uxxvOB+ChE=
|
||||||
|
-----END CERTIFICATE-----
|
82
samples/contacts/etc/ssl/howto.txt
Normal file
82
samples/contacts/etc/ssl/howto.txt
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
$Id$
|
||||||
|
|
||||||
|
CAS requires HTTPS be used for all operations, with the certificate used
|
||||||
|
having been signed by a certificate in the cacerts files shipped with Java.
|
||||||
|
|
||||||
|
If you're using a HTTPS certificate signed by a well known authority
|
||||||
|
(like Verisign), you can safely ignore the procedure below (although you
|
||||||
|
might find the troubleshooting section at the end helpful).
|
||||||
|
|
||||||
|
The following demonstrates how to create a self-signed certificate and add
|
||||||
|
it to the cacerts file. If you just want to use the certificate we have
|
||||||
|
already created and shipped with the Acegi Security System for Spring, you
|
||||||
|
can skip directly to step 3.
|
||||||
|
|
||||||
|
|
||||||
|
1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password
|
||||||
|
|
||||||
|
What is your first and last name?
|
||||||
|
[Unknown]: localhost
|
||||||
|
What is the name of your organizational unit?
|
||||||
|
[Unknown]: Acegi Security System for Spring
|
||||||
|
What is the name of your organization?
|
||||||
|
[Unknown]: TEST CERTIFICATE ONLY. DO NOT USE IN PRODUCTION.
|
||||||
|
What is the name of your City or Locality?
|
||||||
|
[Unknown]:
|
||||||
|
What is the name of your State or Province?
|
||||||
|
[Unknown]:
|
||||||
|
What is the two-letter country code for this unit?
|
||||||
|
[Unknown]:
|
||||||
|
Is CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ONLY. D
|
||||||
|
O NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown correct?
|
||||||
|
[no]: yes
|
||||||
|
|
||||||
|
|
||||||
|
2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password
|
||||||
|
|
||||||
|
3. copy acegisecurity.txt %JAVA_HOME%\lib\security
|
||||||
|
|
||||||
|
4. copy keystore %YOUR_WEB_CONTAINER_LOCATION%
|
||||||
|
|
||||||
|
NOTE: You will need to configure your web container as appropriate.
|
||||||
|
We recommend you test the certificate works by visiting
|
||||||
|
https://localhost:8443. When prompted by your browser, select to
|
||||||
|
install the certificate.
|
||||||
|
|
||||||
|
5. cd %JAVA_HOME%\lib\security
|
||||||
|
|
||||||
|
6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity
|
||||||
|
|
||||||
|
Owner: CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ONL
|
||||||
|
Y. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
|
||||||
|
Issuer: CN=localhost, OU=Acegi Security System for Spring, O=TEST CERTIFICATE ON
|
||||||
|
LY. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
|
||||||
|
Serial number: 4080daf4
|
||||||
|
Valid from: Sat Apr 17 07:21:24 GMT 2004 until: Tue Sep 02 07:21:24 GMT 2031
|
||||||
|
Certificate fingerprints:
|
||||||
|
MD5: B4:AC:A8:24:34:99:F1:A9:F8:1D:A5:6C:BF:0A:34:FA
|
||||||
|
SHA1: F1:E6:B1:3A:01:39:2D:CF:06:FA:82:AB:86:0D:77:9D:06:93:D6:B0
|
||||||
|
Trust this certificate? [no]: yes
|
||||||
|
Certificate was added to keystore
|
||||||
|
[Saving cacerts]
|
||||||
|
|
||||||
|
|
||||||
|
7. Finished. You can now run the sample application as if you purchased a
|
||||||
|
properly signed certificate. For production applications, of course you should
|
||||||
|
use an appropriately signed certificate so your web visitors will trust it
|
||||||
|
(such as issued by Thawte, Verisign etc).
|
||||||
|
|
||||||
|
TROUBLESHOOTING
|
||||||
|
|
||||||
|
* A "sun.security.validator.ValidatorException: No trusted certificate
|
||||||
|
found" indicates the cacerts is not being used or it did not correctly
|
||||||
|
import the certificate. To rule out your web container replacing or in
|
||||||
|
some way modifying the trust manager, set the
|
||||||
|
CasAuthenticationProvider.trustStore property to the full file system
|
||||||
|
location to your cacerts file.
|
||||||
|
|
||||||
|
* If your web container is ignoring your cacerts file, double-check it
|
||||||
|
is stored in $JAVA_HOME\lib\security\cacerts. $JAVA_HOME might be
|
||||||
|
pointing to the SDK, not JRE. In that case, copy
|
||||||
|
$JAVA_HOME\jre\lib\security\cacerts to $JAVA_HOME\lib\security\cacerts
|
||||||
|
|
BIN
samples/contacts/etc/ssl/keystore
Normal file
BIN
samples/contacts/etc/ssl/keystore
Normal file
Binary file not shown.
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
name.filter=contacts
|
name.filter=contacts
|
||||||
name.ca=contacts-container-adapter
|
name.ca=contacts-container-adapter
|
||||||
|
name.cas=contacts-cas
|
||||||
src.dir=src
|
src.dir=src
|
||||||
war.dir=war
|
war.dir=war
|
||||||
lib.dir=${basedir}/../../lib
|
lib.dir=${basedir}/../../lib
|
||||||
|
Loading…
x
Reference in New Issue
Block a user