1
0
mirror of synced 2026-08-04 17:27:13 +00:00

SEC-674: Moved cas "ui" package to new module

This commit is contained in:
Luke Taylor
2008-02-22 11:11:56 +00:00
parent 2dd9faabc0
commit ca9e64f857
8 changed files with 5 additions and 0 deletions
@@ -1,90 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.ui.cas;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrder;
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 opaque 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
* org.springframework.security.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 ========================================================================================================
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);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* This filter by default responds to <code>/j_spring_cas_security_check</code>.
*
* @return the default
*/
public String getDefaultFilterProcessesUrl() {
return "/j_spring_cas_security_check";
}
public int getOrder() {
return FilterChainOrder.CAS_PROCESSING_FILTER;
}
}
@@ -1,111 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.ui.cas;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.AuthenticationException;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).<P>The user's browser will be redirected to the JA-SIG 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;
/**
* Determines whether the Service URL should include the session id for the specific user. As of CAS 3.0.5, the
* session id will automatically be stripped. However, older versions of CAS (i.e. CAS 2), do not automatically
* strip the session identifier (this is a bug on the part of the older server implementations), so an option to
* disable the session encoding is provided for backwards compatibility.
*
* By default, encoding is enabled.
*/
private boolean encodeServiceUrlWithSessionId = true;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
}
public void commence(final ServletRequest servletRequest, final ServletResponse servletResponse,
final AuthenticationException authenticationException)
throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String urlEncodedService = this.encodeServiceUrlWithSessionId ? response.encodeURL(this.serviceProperties.getService()) : this.serviceProperties.getService();
final StringBuffer buffer = new StringBuffer(255);
synchronized (buffer) {
buffer.append(this.loginUrl);
buffer.append("?service=");
buffer.append(URLEncoder.encode(urlEncodedService, "UTF-8"));
buffer.append(this.serviceProperties.isSendRenew() ? "&renew=true" : "");
}
response.sendRedirect(buffer.toString());
}
/**
* 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 this.loginUrl;
}
public ServiceProperties getServiceProperties() {
return this.serviceProperties;
}
public void setLoginUrl(final String loginUrl) {
this.loginUrl = loginUrl;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void setEncodeServiceUrlWithSessionId(final boolean encodeServiceUrlWithSessionId) {
this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;
}
}
@@ -1,78 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.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 Spring Security.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class ServiceProperties implements InitializingBean {
//~ Instance fields ================================================================================================
private String service;
private boolean sendRenew = false;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
if ((service == null) || "".equals(service)) {
throw new IllegalArgumentException("service must be specified");
}
}
/**
* Represents the service the user is authenticating to.
* <p>This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* For example,
* <pre>
* https://www.mycompany.com/application/j_spring_cas_security_check
* </pre>
*
* @return the URL of the service the user is authenticating to
*/
public String getService() {
return service;
}
/**
* 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.</p>
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
public boolean isSendRenew() {
return sendRenew;
}
public void setSendRenew(boolean sendRenew) {
this.sendRenew = sendRenew;
}
public void setService(String service) {
this.service = service;
}
}
@@ -1,6 +0,0 @@
<html>
<body>
Authenticates standard web browser users via
JA-SIG Central Authentication Service (CAS).
</body>
</html>
@@ -1,128 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.ui.cas;
import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.net.URLEncoder;
/**
* 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 static void main(String[] args) {
junit.textui.TestRunner.run(CasProcessingFilterEntryPointTests.class);
}
public final void setUp() throws Exception {
super.setUp();
}
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_spring_cas_security_check");
CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
ep.setLoginUrl("https://cas/login");
ep.setServiceProperties(sp);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/some_path");
MockHttpServletResponse response = new MockHttpServletResponse();
ep.afterPropertiesSet();
ep.commence(request, response, null);
assertEquals("https://cas/login?service="
+ URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8"),
response.getRedirectedUrl());
}
public void testNormalOperationWithRenewTrue() throws Exception {
ServiceProperties sp = new ServiceProperties();
sp.setSendRenew(true);
sp.setService("https://mycompany.com/bigWebApp/j_spring_cas_security_check");
CasProcessingFilterEntryPoint ep = new CasProcessingFilterEntryPoint();
ep.setLoginUrl("https://cas/login");
ep.setServiceProperties(sp);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/some_path");
MockHttpServletResponse response = new MockHttpServletResponse();
ep.afterPropertiesSet();
ep.commence(request, response, null);
assertEquals("https://cas/login?service="
+ URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8") + "&renew=true",
response.getRedirectedUrl());
}
}
@@ -1,90 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.ui.cas;
import junit.framework.TestCase;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.MockAuthenticationManager;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* 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 static void main(String[] args) {
junit.textui.TestRunner.run(CasProcessingFilterTests.class);
}
public final void setUp() throws Exception {
super.setUp();
}
public void testGetters() {
CasProcessingFilter filter = new CasProcessingFilter();
assertEquals("/j_spring_cas_security_check", filter.getDefaultFilterProcessesUrl());
}
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addParameter("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();
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);
}
}
}
@@ -1,71 +0,0 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.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 static void main(String[] args) {
junit.textui.TestRunner.run(ServicePropertiesTests.class);
}
public final void setUp() throws Exception {
super.setUp();
}
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();
}
}