From d81a806405d78755854bde90457c80546027d69a Mon Sep 17 00:00:00 2001 From: Ray Krueger Date: Fri, 20 Apr 2007 14:47:04 +0000 Subject: [PATCH] OpenID support contributed by Robin Bramley Todo: * Improve Test Coverage * Replace Servlet with Filter * Add support for providers other than JanRain as it may be dead --- pom.xml | 3 + sandbox/openid/pom.xml | 83 ++++++++ .../AuthenticationCancelledException.java | 36 ++++ .../openid/OpenIDAuthenticationProvider.java | 108 ++++++++++ .../openid/OpenIDAuthenticationStatus.java | 69 ++++++ .../openid/OpenIDAuthenticationToken.java | 88 ++++++++ .../providers/openid/package.html | 6 + .../ui/openid/OpenIDConstants.java | 26 +++ .../ui/openid/OpenIDConsumer.java | 62 ++++++ .../ui/openid/OpenIDConsumerException.java | 32 +++ .../openid/OpenIDLoginInitiationServlet.java | 180 ++++++++++++++++ .../OpenIDResponseProcessingFilter.java | 84 ++++++++ .../consumers/JanRainOpenIDConsumer.java | 201 ++++++++++++++++++ .../ui/openid/consumers/package.html | 5 + .../org/acegisecurity/ui/openid/package.html | 5 + .../openid/MockAuthoritiesPopulator.java | 40 ++++ .../OpenIDAuthenticationProviderTests.java | 194 +++++++++++++++++ .../OpenIDResponseProcessingFilterTests.java | 139 ++++++++++++ .../openid/consumers/MockOpenIDConsumer.java | 78 +++++++ sandbox/pom.xml | 1 + 20 files changed, 1440 insertions(+) create mode 100644 sandbox/openid/pom.xml create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/providers/openid/AuthenticationCancelledException.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationStatus.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationToken.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/providers/openid/package.html create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConstants.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumerException.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilter.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/JanRainOpenIDConsumer.java create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/package.html create mode 100644 sandbox/openid/src/main/java/org/acegisecurity/ui/openid/package.html create mode 100644 sandbox/openid/src/test/java/org/acegisecurity/providers/openid/MockAuthoritiesPopulator.java create mode 100644 sandbox/openid/src/test/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProviderTests.java create mode 100644 sandbox/openid/src/test/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilterTests.java create mode 100644 sandbox/openid/src/test/java/org/acegisecurity/ui/openid/consumers/MockOpenIDConsumer.java diff --git a/pom.xml b/pom.xml index e131c96cae..16de3ffc22 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,9 @@ Mike Perham + + Robin Bramley + diff --git a/sandbox/openid/pom.xml b/sandbox/openid/pom.xml new file mode 100644 index 0000000000..1edfd3117f --- /dev/null +++ b/sandbox/openid/pom.xml @@ -0,0 +1,83 @@ + + 4.0.0 + + org.acegisecurity + acegi-security-sandbox + 1.0.2 + + acegi-security-openid + Acegi Security System for Spring - OpenID support + Acegi Security System for Spring - Support for OpenID + 0.1-SNAPSHOT + + + + AcegiMaven + Acegi 3rd party repository + http://acegisecurity.sourceforge.net/maven + + true + + + + + + scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/sandbox/openid + + + scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/sandbox/openid + + http://svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/sandbox/openid/ + + + + + org.springframework + spring-mock + true + + + com.janrain + Janrain-Openid + 20070226 + + + gnu + libidn + 0.6.3 + + + org.apache.geronimo.specs + geronimo-servlet_2.4_spec + 1.1.1 + compile + true + + + + + + + diff --git a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/AuthenticationCancelledException.java b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/AuthenticationCancelledException.java new file mode 100644 index 0000000000..7f68b5ce92 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/AuthenticationCancelledException.java @@ -0,0 +1,36 @@ +/* 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.acegisecurity.providers.openid; + +import org.acegisecurity.AuthenticationException; + + +/** + * Indicates that OpenID authentication was cancelled + * + * @author Robin Bramley, Opsera Ltd + * @version $Id:$ + */ +public class AuthenticationCancelledException extends AuthenticationException { + //~ Constructors =================================================================================================== + + public AuthenticationCancelledException(String msg) { + super(msg); + } + + public AuthenticationCancelledException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java new file mode 100644 index 0000000000..516c9cabf1 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java @@ -0,0 +1,108 @@ +/* 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.acegisecurity.providers.openid; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationServiceException; +import org.acegisecurity.BadCredentialsException; + +import org.acegisecurity.providers.AuthenticationProvider; +import org.acegisecurity.providers.cas.CasAuthoritiesPopulator; + +import org.acegisecurity.userdetails.UserDetails; + +import org.springframework.beans.factory.InitializingBean; + +import org.springframework.util.Assert; + + +/** + * Finalises the OpenID authentication by obtaining local roles + * + * @author Robin Bramley, Opsera Ltd. + */ +public class OpenIDAuthenticationProvider implements AuthenticationProvider, InitializingBean { + //~ Instance fields ================================================================================================ + + private CasAuthoritiesPopulator ssoAuthoritiesPopulator; + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.ssoAuthoritiesPopulator, "The ssoAuthoritiesPopulator must be set"); + } + + /* (non-Javadoc) + * @see org.acegisecurity.providers.AuthenticationProvider#authenticate(org.acegisecurity.Authentication) + */ + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + if (!supports(authentication.getClass())) { + return null; + } + + if (authentication instanceof OpenIDAuthenticationToken) { + OpenIDAuthenticationToken response = (OpenIDAuthenticationToken) authentication; + OpenIDAuthenticationStatus status = response.getStatus(); + + // handle the various possibilites + if (status == OpenIDAuthenticationStatus.SUCCESS) { + //String message = "Log in succeeded: ";// + savedId; + + /* TODO: allow for regex for mapping URL + * e.g. http://mydomain.com/username + * or http://{username}.mydomain.com + */ + + // Lookup user details + UserDetails userDetails = this.ssoAuthoritiesPopulator.getUserDetails(response.getIdentityUrl()); + + authentication = new OpenIDAuthenticationToken(userDetails.getAuthorities(), response.getStatus(), + response.getIdentityUrl()); + + return authentication; + } else if (status == OpenIDAuthenticationStatus.CANCELLED) { + throw new AuthenticationCancelledException("Log in cancelled"); + } else if (status == OpenIDAuthenticationStatus.ERROR) { + throw new AuthenticationServiceException("Error message from server: " + response.getMessage()); + } else if (status == OpenIDAuthenticationStatus.FAILURE) { + throw new BadCredentialsException("Log in failed - identity could not be verified"); + } else if (status == OpenIDAuthenticationStatus.SETUP_NEEDED) { + throw new AuthenticationServiceException( + "The server responded setup was needed, which shouldn't happen"); + } else { + throw new AuthenticationServiceException("Unrecognized return value " + status.toString()); + } + } + + return null; + } + + public void setSsoAuthoritiesPopulator(CasAuthoritiesPopulator ssoAuthoritiesPopulator) { + this.ssoAuthoritiesPopulator = ssoAuthoritiesPopulator; + } + + /* (non-Javadoc) + * @see org.acegisecurity.providers.AuthenticationProvider#supports(java.lang.Class) + */ + public boolean supports(Class authentication) { + if (OpenIDAuthenticationToken.class.isAssignableFrom(authentication)) { + return true; + } else { + return false; + } + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationStatus.java b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationStatus.java new file mode 100644 index 0000000000..1ad165d7ce --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationStatus.java @@ -0,0 +1,69 @@ +/* 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.acegisecurity.providers.openid; + +import java.io.ObjectStreamException; +import java.io.Serializable; + + +/** + * Based on JanRain status codes + * + * @author JanRain Inc. + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDAuthenticationStatus implements Serializable { + //~ Static fields/initializers ===================================================================================== + + private static final long serialVersionUID = -998877665544332211L; + private static int nextOrdinal = 0; + + /** This code indicates a successful authentication request */ + public static final OpenIDAuthenticationStatus SUCCESS = new OpenIDAuthenticationStatus("success"); + + /** This code indicates a failed authentication request */ + public static final OpenIDAuthenticationStatus FAILURE = new OpenIDAuthenticationStatus("failure"); + + /** This code indicates the server reported an error */ + public static final OpenIDAuthenticationStatus ERROR = new OpenIDAuthenticationStatus("error"); + + /** This code indicates that the user needs to do additional work to prove their identity */ + public static final OpenIDAuthenticationStatus SETUP_NEEDED = new OpenIDAuthenticationStatus("setup needed"); + + /** This code indicates that the user cancelled their login request */ + public static final OpenIDAuthenticationStatus CANCELLED = new OpenIDAuthenticationStatus("cancelled"); + private static final OpenIDAuthenticationStatus[] PRIVATE_VALUES = {SUCCESS, FAILURE, ERROR, SETUP_NEEDED, CANCELLED}; + + //~ Instance fields ================================================================================================ + + private String name; + private final int ordinal = nextOrdinal++; + + //~ Constructors =================================================================================================== + + private OpenIDAuthenticationStatus(String name) { + this.name = name; + } + + //~ Methods ======================================================================================================== + + private Object readResolve() throws ObjectStreamException { + return PRIVATE_VALUES[ordinal]; + } + + public String toString() { + return name; + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationToken.java b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationToken.java new file mode 100644 index 0000000000..75288b020f --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationToken.java @@ -0,0 +1,88 @@ +/* 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.acegisecurity.providers.openid; + +import org.acegisecurity.GrantedAuthority; + +import org.acegisecurity.providers.AbstractAuthenticationToken; + + +/** + * OpenID Authentication Token + * + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDAuthenticationToken extends AbstractAuthenticationToken { + //~ Instance fields ================================================================================================ + + private OpenIDAuthenticationStatus status; + private String identityUrl; + private String message; + + //~ Constructors =================================================================================================== + + public OpenIDAuthenticationToken(OpenIDAuthenticationStatus status, String identityUrl, String message) { + super(new GrantedAuthority[0]); + this.status = status; + this.identityUrl = identityUrl; + this.message = message; + setAuthenticated(false); + } + +/** + * Created by the OpenIDAuthenticationProvider on successful authentication. + * Do not use directly + * + * @param authorities + * @param status + * @param identityUrl + */ + public OpenIDAuthenticationToken(GrantedAuthority[] authorities, OpenIDAuthenticationStatus status, + String identityUrl) { + super(authorities); + this.status = status; + this.identityUrl = identityUrl; + + setAuthenticated(true); + } + + //~ Methods ======================================================================================================== + + /* (non-Javadoc) + * @see org.acegisecurity.Authentication#getCredentials() + */ + public Object getCredentials() { + return null; + } + + public String getIdentityUrl() { + return identityUrl; + } + + public String getMessage() { + return message; + } + + /* (non-Javadoc) + * @see org.acegisecurity.Authentication#getPrincipal() + */ + public Object getPrincipal() { + return identityUrl; + } + + public OpenIDAuthenticationStatus getStatus() { + return status; + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/package.html b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/package.html new file mode 100644 index 0000000000..5fc7ffefe9 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/package.html @@ -0,0 +1,6 @@ + + +An authentication provider that can process OpenID +Authentication Tokens as created by implementations of the OpenIDConsumer interface. + + diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConstants.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConstants.java new file mode 100644 index 0000000000..10920d1728 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConstants.java @@ -0,0 +1,26 @@ +/* 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.acegisecurity.ui.openid; + +/** + * Constants required by OpenID classes + * + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDConstants { + //~ Static fields/initializers ===================================================================================== + + public static final String OPENID_SESSION_MAP_KEY = "openid.session"; +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java new file mode 100644 index 0000000000..a28b2d7674 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java @@ -0,0 +1,62 @@ +/* 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.acegisecurity.ui.openid; + +import org.acegisecurity.providers.openid.OpenIDAuthenticationToken; + +import javax.servlet.http.HttpServletRequest; + + +/** + * An interface for OpenID library implementations + * + * @author Robin Bramley, Opsera Ltd + * + */ +public interface OpenIDConsumer { + //~ Methods ======================================================================================================== + + /** + * Start the authentication process + * + * @param req + * @param identityUrl + * + * @return redirection URL + * + * @throws OpenIDConsumerException + */ + public String beginConsumption(HttpServletRequest req, String identityUrl) + throws OpenIDConsumerException; + + /** + * DOCUMENT ME! + * + * @param req + * + * @return + * + * @throws OpenIDConsumerException + */ + public OpenIDAuthenticationToken endConsumption(HttpServletRequest req) + throws OpenIDConsumerException; + + /** + * DOCUMENT ME! + * + * @param returnToUrl + */ + public void setReturnToUrl(String returnToUrl); +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumerException.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumerException.java new file mode 100644 index 0000000000..8957dee802 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumerException.java @@ -0,0 +1,32 @@ +/* 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.acegisecurity.ui.openid; + +/** + * Thrown by an OpenIDConsumer if it cannot process a request + * + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDConsumerException extends Exception { + //~ Constructors =================================================================================================== + + public OpenIDConsumerException(String message) { + super(message); + } + + public OpenIDConsumerException(String message, Throwable t) { + super(message, t); + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java new file mode 100644 index 0000000000..eacf237e4c --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java @@ -0,0 +1,180 @@ +/* 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.acegisecurity.ui.openid; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.StringUtils; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +/** + * This servlet starts the OpenID authentication process. + *
+ *
Sample web.xml configuration: + *
+ *
<servlet> + *
   <servlet-name>openid</servlet-name> + *
   <servlet-class>org.acegisecurity.ui.openid.OpenIDLoginInitiationServlet</servlet-class> + *
   <load-on-startup>1</load-on-startup> + *
   <init-param> + *
     <description>The error page - will receive error "message"</description> + *
     <param-name>errorPage</param-name> + *
     <param-value>index.jsp</param-value> + *
   </init-param> + *
</servlet> + *
<servlet-mapping> + *
   <servlet-name>openid</servlet-name> + *
   <url-pattern>/j_acegi_openid_start</url-pattern> + *
</servlet-mapping> + *
+ *
Sample login form: + *
<form method="POST" action="j_acegi_openid_start"> + *
   <input type="text" name="j_username" /> + *
   <input type="password" name="j_password" /> + *
   <input type="submit" value="Verify" /> + *
</form> + *
+ *
Usage notes: + *
  • Requires an openIDConsumer Spring bean implementing the {@link OpenIDConsumer} interface
  • + *
  • It will pass off to standard form-based authentication if appropriate
  • + * (note that AuthenticationProcessingFilter requires j_username, j_password) + *
    + *
    Outstanding items: + * TODO: config flag for whether OpenID only or dual mode? + * TODO: username matching logic + * + * @author Robin Bramley, Opsera Ltd + * @version $Id:$ + */ +public class OpenIDLoginInitiationServlet extends HttpServlet { + final static long serialVersionUID = -997766L; + private static final Log logger = LogFactory.getLog(OpenIDLoginInitiationServlet.class); + private static final String passwordField = "j_password"; + + /** + * Servlet config key for looking up the the HttpServletRequest parameter name + * containing the OpenID Identity URL from the Servlet config. + *
    Only set the identityField servlet init-param if you are not using j_username + *
    + *
       <init-param> + *
         <description>The identity form field parameter</description> + *
         <param-name>identityField</param-name> + *
         <param-value>/openid_url</param-value> + *
       </init-param> + */ + public static final String IDENTITY_FIELD_KEY = "identityField"; + + /** + * Servlet config key for the return to URL + */ + public static final String ERROR_PAGE_KEY = "errorPage"; + + /** + * Servlet config key for looking up the form login URL from the Servlet config. + *
    Only set the formLogin servlet init-param if you are not using /j_acegi_security_check + *
    + *
       <init-param> + *
         <description>The form login URL - for standard authentication</description> + *
         <param-name>formLogin</param-name> + *
         <param-value>/custom_acegi_security_check</param-value> + *
       </init-param> + */ + public static final String FORM_LOGIN_URL_KEY = "formLogin"; + + /** + * Spring context key for the OpenID consumer bean + */ + public static final String CONSUMER_KEY = "openIDConsumer"; + private String errorPage = "index.jsp"; + private String identityField = "j_username"; + private String formLoginUrl = "/j_acegi_security_check"; + + /** + * Check for init-params + * + * @Override + */ + public void init() throws ServletException { + super.init(); + + String configErrorPage = getServletConfig() + .getInitParameter(ERROR_PAGE_KEY); + + if (StringUtils.hasText(configErrorPage)) { + errorPage = configErrorPage; + } + + String configIdentityField = getServletConfig() + .getInitParameter(IDENTITY_FIELD_KEY); + + if (StringUtils.hasText(configIdentityField)) { + identityField = configIdentityField; + } + + String configFormLoginUrl = getServletConfig() + .getInitParameter(FORM_LOGIN_URL_KEY); + + if (StringUtils.hasText(configFormLoginUrl)) { + formLoginUrl = configFormLoginUrl; + } + } + + /** + * Process the form post - all the work is done by the OpenIDConsumer.beginConsumption method + * + * @Override + */ + protected void doPost(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException { + WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); + OpenIDConsumer consumer = (OpenIDConsumer) webApplicationContext.getBean(CONSUMER_KEY); + + // get the submitted id field + String id = req.getParameter(identityField); + + // assume page will validate? + //TODO: null checking! + + //TODO: pattern matching + String password = req.getParameter(passwordField); + + if ((password != null) && (password.length() > 0)) { + logger.debug("Attempting to authenticate using username/password"); + + // forward to authenticationProcessingFilter (/j_acegi_security_check - depends on param names) + req.getRequestDispatcher(formLoginUrl).forward(req, res); + + } else { + // send the user the redirect url to proceed with OpenID authentication + try { + String redirect = consumer.beginConsumption(req, id); + logger.debug("Redirecting to: " + redirect); + res.sendRedirect(redirect); + } catch (OpenIDConsumerException oice) { + logger.error("Consumer error!", oice); + req.setAttribute("message", oice.getMessage()); + req.getRequestDispatcher(errorPage).forward(req, res); + } + } + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilter.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilter.java new file mode 100644 index 0000000000..9b369201fc --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilter.java @@ -0,0 +1,84 @@ +/* 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.acegisecurity.ui.openid; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationServiceException; + +import org.acegisecurity.providers.openid.OpenIDAuthenticationToken; + +import org.acegisecurity.ui.AbstractProcessingFilter; +import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter; + +import javax.servlet.http.HttpServletRequest; + + +/** + * Process the response from the OpenID server to the returnTo URL. + * + * @author Robin Bramley, Opsera Ltd + * @version $Id:$ + */ +public class OpenIDResponseProcessingFilter extends AbstractProcessingFilter { + //~ Instance fields ================================================================================================ + + private OpenIDConsumer consumer; + + //~ Methods ======================================================================================================== + + /* (non-Javadoc) + * @see org.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest) + * @Override + */ + public Authentication attemptAuthentication(HttpServletRequest req) + throws AuthenticationException { + OpenIDAuthenticationToken token; + + try { + token = consumer.endConsumption(req); + } catch (OpenIDConsumerException oice) { + throw new AuthenticationServiceException("Consumer error", oice); + } + + // delegate to the auth provider + Authentication authentication = this.getAuthenticationManager().authenticate(token); + + if (authentication.isAuthenticated()) { + req.getSession() + .setAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_USERNAME_KEY, token.getIdentityUrl()); + } + + return authentication; + } + + /* (non-Javadoc) + * @see org.acegisecurity.ui.AbstractProcessingFilter#getDefaultFilterProcessesUrl() + * @Override + */ + public String getDefaultFilterProcessesUrl() { + return "/j_acegi_openid_security_check"; + } + + // dependency injection + /** + * DOCUMENT ME! + * + * @param consumer The OpenIDConsumer to set. + */ + public void setConsumer(OpenIDConsumer consumer) { + this.consumer = consumer; + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/JanRainOpenIDConsumer.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/JanRainOpenIDConsumer.java new file mode 100644 index 0000000000..b275ab0095 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/JanRainOpenIDConsumer.java @@ -0,0 +1,201 @@ +/* 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.acegisecurity.ui.openid.consumers; + +import com.janrain.openid.consumer.AuthRequest; +import com.janrain.openid.consumer.Consumer; +import com.janrain.openid.consumer.ErrorResponse; +import com.janrain.openid.consumer.Response; +import com.janrain.openid.consumer.StatusCode; +import com.janrain.openid.store.OpenIDStore; + +import org.acegisecurity.providers.openid.OpenIDAuthenticationStatus; +import org.acegisecurity.providers.openid.OpenIDAuthenticationToken; + +import org.acegisecurity.ui.openid.OpenIDConstants; +import org.acegisecurity.ui.openid.OpenIDConsumer; +import org.acegisecurity.ui.openid.OpenIDConsumerException; + +import org.springframework.beans.factory.InitializingBean; + +import org.springframework.util.Assert; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + + +/** + * OpenIDConsumer implementation using the JanRain OpenID library + * + * @author Robin Bramley, Opsera Ltd + * @version $Id:$ + */ +public class JanRainOpenIDConsumer implements OpenIDConsumer, InitializingBean { + //~ Static fields/initializers ===================================================================================== + + private static final String SAVED_ID_SESSION_KEY = "savedId"; + + //~ Instance fields ================================================================================================ + + private OpenIDStore store; + private String returnToUrl = "j_acegi_openid_security_check"; + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.store, "An OpenIDStore must be set on the store property"); + } + + /* (non-Javadoc) + * @see org.acegisecurity.ui.openid.OpenIDConsumer#beginConsumption(java.lang.String) + */ + public String beginConsumption(HttpServletRequest req, String identityUrl) + throws OpenIDConsumerException { + // fetch/create a session Map for the consumer's use + HttpSession session = req.getSession(); + Map sessionMap = (Map) session.getAttribute(OpenIDConstants.OPENID_SESSION_MAP_KEY); + + if (sessionMap == null) { + sessionMap = new HashMap(); + session.setAttribute(OpenIDConstants.OPENID_SESSION_MAP_KEY, sessionMap); + } + + Consumer openIdConsumer = new Consumer(sessionMap, store); + + // Create an Authrequest object from the submitted value + AuthRequest ar; + + try { + ar = openIdConsumer.begin(identityUrl); + } catch (IOException ioe) { + req.getSession().setAttribute(SAVED_ID_SESSION_KEY, escapeAttr(identityUrl)); + throw new OpenIDConsumerException("Error on begin consumption for " + identityUrl, ioe); + } + + // construct trust root and return to URLs. + String port = ""; + + if (req.getServerPort() != 80) { + port = ":" + req.getServerPort(); + } + + String trustRoot = req.getScheme() + "://" + req.getServerName() + port + "/"; + String cp = req.getContextPath(); + + if (!cp.equals("")) { + cp = cp.substring(1) + "/"; + } + + String returnTo = trustRoot + cp + returnToUrl; + + // send the user the redirect url to proceed with OpenID authentication + return ar.redirectUrl(trustRoot, returnTo); + } + + /* (non-Javadoc) + * @see org.acegisecurity.ui.openid.OpenIDConsumer#endConsumption(javax.servlet.http.HttpServletRequest) + */ + public OpenIDAuthenticationToken endConsumption(HttpServletRequest req) + throws OpenIDConsumerException { + HttpSession session = req.getSession(); + Map sessionMap = (Map) session.getAttribute(OpenIDConstants.OPENID_SESSION_MAP_KEY); + + if (sessionMap == null) { + sessionMap = new HashMap(); + session.setAttribute(OpenIDConstants.OPENID_SESSION_MAP_KEY, sessionMap); + } + + // get a Consumer instance + Consumer openIdConsumer = new Consumer(sessionMap, store); + + // convert the argument map into the form the library uses with a handy + // convenience function + Map query = Consumer.filterArgs(req.getParameterMap()); + + // Check the arguments to see what the response was. + Response response = openIdConsumer.complete(query); + + String message = ""; + OpenIDAuthenticationStatus status; + + StatusCode statusCode = response.getStatus(); + + if (statusCode == StatusCode.CANCELLED) { + status = OpenIDAuthenticationStatus.CANCELLED; + } else if (statusCode == StatusCode.ERROR) { + status = OpenIDAuthenticationStatus.ERROR; + message = ((ErrorResponse) response).getMessage(); + } else if (statusCode == StatusCode.FAILURE) { + status = OpenIDAuthenticationStatus.FAILURE; + } else if (statusCode == StatusCode.SETUP_NEEDED) { + status = OpenIDAuthenticationStatus.SETUP_NEEDED; + } else if (statusCode == StatusCode.SUCCESS) { + status = OpenIDAuthenticationStatus.SUCCESS; + } else { + // unknown status code + throw new OpenIDConsumerException("Unknown response status " + statusCode.toString()); + } + + return new OpenIDAuthenticationToken(status, response.getIdentityUrl(), message); + } + + /* + * This method escapes characters in a string that can cause problems in + * HTML + */ + private String escapeAttr(String s) { + if (s == null) { + return ""; + } + + StringBuffer result = new StringBuffer(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == '<') { + result.append("<"); + } else if (c == '>') { + result.append(">"); + } else if (c == '&') { + result.append("&"); + } else if (c == '\"') { + result.append("""); + } else if (c == '\'') { + result.append("'"); + } else if (c == '\\') { + result.append("\"); + } else { + result.append(c); + } + } + + return result.toString(); + } + + public void setReturnToUrl(String returnToUrl) { + this.returnToUrl = returnToUrl; + } + + // dependency injection + public void setStore(OpenIDStore store) { + this.store = store; + } +} diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/package.html b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/package.html new file mode 100644 index 0000000000..913d6930e9 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/package.html @@ -0,0 +1,5 @@ + + +Implementations of the OpenIDConsumer interface using 3rd party libraries. + + diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/package.html b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/package.html new file mode 100644 index 0000000000..aa8555d718 --- /dev/null +++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/package.html @@ -0,0 +1,5 @@ + + +Authenticates standard web browser users via OpenID. + + \ No newline at end of file diff --git a/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/MockAuthoritiesPopulator.java b/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/MockAuthoritiesPopulator.java new file mode 100644 index 0000000000..5760e978c2 --- /dev/null +++ b/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/MockAuthoritiesPopulator.java @@ -0,0 +1,40 @@ +/* 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.acegisecurity.providers.openid; + +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.GrantedAuthorityImpl; + +import org.acegisecurity.providers.cas.CasAuthoritiesPopulator; + +import org.acegisecurity.userdetails.User; +import org.acegisecurity.userdetails.UserDetails; + + +/** + * DOCUMENT ME! + * + * @author Robin Bramley, Opsera Ltd + */ +public class MockAuthoritiesPopulator implements CasAuthoritiesPopulator { + //~ Methods ======================================================================================================== + + public UserDetails getUserDetails(String ssoUserId) + throws AuthenticationException { + return new User(ssoUserId, "password", true, true, true, true, + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A"), new GrantedAuthorityImpl("ROLE_B")}); + } +} diff --git a/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProviderTests.java b/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProviderTests.java new file mode 100644 index 0000000000..4d77d76103 --- /dev/null +++ b/sandbox/openid/src/test/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProviderTests.java @@ -0,0 +1,194 @@ +/* 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.acegisecurity.providers.openid; + +import junit.framework.TestCase; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationServiceException; +import org.acegisecurity.BadCredentialsException; + +import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; + + +/** + * Tests {@link OpenIDAuthenticationProvider} + * + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDAuthenticationProviderTests extends TestCase { + //~ Static fields/initializers ===================================================================================== + + private static final String USERNAME = "user.acegiopenid.com"; + + //~ Methods ======================================================================================================== + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testAuthenticateCancel() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.CANCELLED, USERNAME, ""); + + assertFalse(preAuth.isAuthenticated()); + + try { + provider.authenticate(preAuth); + fail("Should throw an AuthenticationException"); + } catch (AuthenticationCancelledException expected) { + assertEquals("Log in cancelled", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testAuthenticateError() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.ERROR, USERNAME, ""); + + assertFalse(preAuth.isAuthenticated()); + + try { + provider.authenticate(preAuth); + fail("Should throw an AuthenticationException"); + } catch (AuthenticationServiceException expected) { + assertEquals("Error message from server: ", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testAuthenticateFailure() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE, USERNAME, ""); + + assertFalse(preAuth.isAuthenticated()); + + try { + provider.authenticate(preAuth); + fail("Should throw an AuthenticationException"); + } catch (BadCredentialsException expected) { + assertEquals("Log in failed - identity could not be verified", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testAuthenticateSetupNeeded() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SETUP_NEEDED, USERNAME, ""); + + assertFalse(preAuth.isAuthenticated()); + + try { + provider.authenticate(preAuth); + fail("Should throw an AuthenticationException"); + } catch (AuthenticationServiceException expected) { + assertEquals("The server responded setup was needed, which shouldn't happen", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testAuthenticateSuccess() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, USERNAME, ""); + + assertFalse(preAuth.isAuthenticated()); + + Authentication postAuth = provider.authenticate(preAuth); + + assertNotNull(postAuth); + assertTrue(postAuth instanceof OpenIDAuthenticationToken); + assertTrue(postAuth.isAuthenticated()); + assertNotNull(postAuth.getPrincipal()); + assertEquals(preAuth.getPrincipal(), postAuth.getPrincipal()); + assertNotNull(postAuth.getAuthorities()); + assertTrue(postAuth.getAuthorities().length > 0); + assertTrue(((OpenIDAuthenticationToken) postAuth).getStatus() == OpenIDAuthenticationStatus.SUCCESS); + assertTrue(((OpenIDAuthenticationToken) postAuth).getMessage() == null); + } + + public void testDetectsMissingAuthoritiesPopulator() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + + try { + provider.afterPropertiesSet(); + fail("Should have thrown Exception"); + } catch (Exception expected) { + assertEquals("The ssoAuthoritiesPopulator must be set", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.supports(Class)' + */ + public void testDoesntSupport() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + assertFalse(provider.supports(UsernamePasswordAuthenticationToken.class)); + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.authenticate(Authentication)' + */ + public void testIgnoresUserPassAuthToken() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(USERNAME, "password"); + assertEquals(null, provider.authenticate(token)); + } + + /* + * Test method for 'org.acegisecurity.providers.openid.OpenIDAuthenticationProvider.supports(Class)' + */ + public void testSupports() { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + + assertTrue(provider.supports(OpenIDAuthenticationToken.class)); + } + + public void testValidation() throws Exception { + OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider(); + provider.setSsoAuthoritiesPopulator(new MockAuthoritiesPopulator()); + provider.afterPropertiesSet(); + + provider.setSsoAuthoritiesPopulator(null); + + try { + provider.afterPropertiesSet(); + fail("IllegalArgumentException expected, ssoAuthoritiesPopulator is null"); + } catch (IllegalArgumentException e) { + //expected + } + } +} diff --git a/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilterTests.java b/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilterTests.java new file mode 100644 index 0000000000..6c2046d6e4 --- /dev/null +++ b/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/OpenIDResponseProcessingFilterTests.java @@ -0,0 +1,139 @@ +/* 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.acegisecurity.ui.openid; + +import junit.framework.TestCase; + +import org.acegisecurity.AbstractAuthenticationManager; +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.BadCredentialsException; + +import org.acegisecurity.providers.cas.CasAuthoritiesPopulator; +import org.acegisecurity.providers.openid.MockAuthoritiesPopulator; +import org.acegisecurity.providers.openid.OpenIDAuthenticationStatus; +import org.acegisecurity.providers.openid.OpenIDAuthenticationToken; + +import org.acegisecurity.ui.openid.consumers.MockOpenIDConsumer; + +import org.springframework.mock.web.MockHttpServletRequest; + + +/** + * Tests {@link OpenIDResponseProcessingFilter} + * + * @author Robin Bramley, Opsera Ltd + */ +public class OpenIDResponseProcessingFilterTests extends TestCase { + //~ Static fields/initializers ===================================================================================== + + private static final String USERNAME = "user.acegiopenid.com"; + + //~ Methods ======================================================================================================== + + /* + * Test method for 'org.acegisecurity.ui.openid.OpenIDResponseProcessingFilter.attemptAuthentication(HttpServletRequest)' + */ + public void testAttemptAuthenticationFailure() { + // set up mock objects + MockOpenIDAuthenticationManager mockAuthManager = new MockOpenIDAuthenticationManager(false); + + OpenIDAuthenticationToken token = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE, USERNAME, ""); + MockOpenIDConsumer mockConsumer = new MockOpenIDConsumer(); + mockConsumer.setToken(token); + + MockHttpServletRequest req = new MockHttpServletRequest(); + + OpenIDResponseProcessingFilter filter = new OpenIDResponseProcessingFilter(); + filter.setConsumer(mockConsumer); + filter.setAuthenticationManager(mockAuthManager); + + // run test + try { + filter.attemptAuthentication(req); + fail("Should've thrown exception"); + } catch (BadCredentialsException expected) { + assertEquals("MockOpenIDAuthenticationManager instructed to deny access", expected.getMessage()); + } + } + + /* + * Test method for 'org.acegisecurity.ui.openid.OpenIDResponseProcessingFilter.attemptAuthentication(HttpServletRequest)' + */ + public void testAttemptAuthenticationHttpServletRequest() { + // set up mock objects + MockOpenIDAuthenticationManager mockAuthManager = new MockOpenIDAuthenticationManager(true); + + OpenIDAuthenticationToken token = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, USERNAME, ""); + MockOpenIDConsumer mockConsumer = new MockOpenIDConsumer(); + mockConsumer.setToken(token); + + MockHttpServletRequest req = new MockHttpServletRequest(); + + OpenIDResponseProcessingFilter filter = new OpenIDResponseProcessingFilter(); + filter.setConsumer(mockConsumer); + filter.setAuthenticationManager(mockAuthManager); + + // run test + Authentication authentication = filter.attemptAuthentication(req); + + // assertions + assertNotNull(authentication); + assertTrue(authentication.isAuthenticated()); + assertTrue(authentication instanceof OpenIDAuthenticationToken); + assertNotNull(authentication.getPrincipal()); + assertEquals(USERNAME, authentication.getPrincipal()); + assertNotNull(authentication.getAuthorities()); + assertTrue(authentication.getAuthorities().length > 0); + assertTrue(((OpenIDAuthenticationToken) authentication).getStatus() == OpenIDAuthenticationStatus.SUCCESS); + assertTrue(((OpenIDAuthenticationToken) authentication).getMessage() == null); + } + + /* + * Test method for 'org.acegisecurity.ui.openid.OpenIDResponseProcessingFilter.getDefaultFilterProcessesUrl()' + */ + public void testGetDefaultFilterProcessesUrl() { + OpenIDResponseProcessingFilter filter = new OpenIDResponseProcessingFilter(); + assertEquals("/j_acegi_openid_security_check", filter.getDefaultFilterProcessesUrl()); + } + + //~ Inner Classes ================================================================================================== + + // private mock AuthenticationManager + private class MockOpenIDAuthenticationManager extends AbstractAuthenticationManager { + private CasAuthoritiesPopulator ssoAuthoritiesPopulator; + private boolean grantAccess = true; + + public MockOpenIDAuthenticationManager(boolean grantAccess) { + this.grantAccess = grantAccess; + ssoAuthoritiesPopulator = new MockAuthoritiesPopulator(); + } + + public MockOpenIDAuthenticationManager() { + super(); + ssoAuthoritiesPopulator = new MockAuthoritiesPopulator(); + } + + public Authentication doAuthentication(Authentication authentication) + throws AuthenticationException { + if (grantAccess) { + return new OpenIDAuthenticationToken(ssoAuthoritiesPopulator.getUserDetails(USERNAME).getAuthorities(), + OpenIDAuthenticationStatus.SUCCESS, USERNAME); + } else { + throw new BadCredentialsException("MockOpenIDAuthenticationManager instructed to deny access"); + } + } + } +} diff --git a/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/consumers/MockOpenIDConsumer.java b/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/consumers/MockOpenIDConsumer.java new file mode 100644 index 0000000000..616d63ae06 --- /dev/null +++ b/sandbox/openid/src/test/java/org/acegisecurity/ui/openid/consumers/MockOpenIDConsumer.java @@ -0,0 +1,78 @@ +/* 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.acegisecurity.ui.openid.consumers; + +import org.acegisecurity.providers.openid.OpenIDAuthenticationToken; + +import org.acegisecurity.ui.openid.OpenIDConsumer; +import org.acegisecurity.ui.openid.OpenIDConsumerException; + +import javax.servlet.http.HttpServletRequest; + + +/** + * DOCUMENT ME! + * + * @author Robin Bramley, Opsera Ltd + */ +public class MockOpenIDConsumer implements OpenIDConsumer { + //~ Instance fields ================================================================================================ + + private OpenIDAuthenticationToken token; + private String redirectUrl; + + //~ Methods ======================================================================================================== + + /* (non-Javadoc) + * @see org.acegisecurity.ui.openid.OpenIDConsumer#beginConsumption(javax.servlet.http.HttpServletRequest, java.lang.String) + */ + public String beginConsumption(HttpServletRequest req, String identityUrl) + throws OpenIDConsumerException { + return redirectUrl; + } + + /* (non-Javadoc) + * @see org.acegisecurity.ui.openid.OpenIDConsumer#endConsumption(javax.servlet.http.HttpServletRequest) + */ + public OpenIDAuthenticationToken endConsumption(HttpServletRequest req) + throws OpenIDConsumerException { + return token; + } + + /** + * Set the redirectUrl to be returned by beginConsumption + * + * @param redirectUrl + */ + public void setRedirectUrl(String redirectUrl) { + this.redirectUrl = redirectUrl; + } + + /* (non-Javadoc) + * @see org.acegisecurity.ui.openid.OpenIDConsumer#setReturnToUrl(java.lang.String) + */ + public void setReturnToUrl(String returnToUrl) { + // TODO Auto-generated method stub + } + + /** + * Set the token to be returned by endConsumption + * + * @param token + */ + public void setToken(OpenIDAuthenticationToken token) { + this.token = token; + } +} diff --git a/sandbox/pom.xml b/sandbox/pom.xml index 77c17d8eed..7d37f589ea 100644 --- a/sandbox/pom.xml +++ b/sandbox/pom.xml @@ -20,6 +20,7 @@ webwork + openid other