diff --git a/sandbox/openid/pom.xml b/sandbox/openid/pom.xml
index 252a3b720b..7c16655339 100644
--- a/sandbox/openid/pom.xml
+++ b/sandbox/openid/pom.xml
@@ -4,7 +4,7 @@
org.acegisecurity
acegi-security-sandbox
- 1.0.4-SNAPSHOT
+ 1.0.5-SNAPSHOT
acegi-security-openid
Acegi Security System for Spring - OpenID support
@@ -37,6 +37,11 @@
spring-mock
true
+
+ org.openid4java
+ openid4java
+ 0.9.2
+
com.janrain
Janrain-Openid
@@ -62,6 +67,98 @@
provided
-->
+
+
+
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
index 516c9cabf1..20e87e620b 100644
--- a/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java
+++ b/sandbox/openid/src/main/java/org/acegisecurity/providers/openid/OpenIDAuthenticationProvider.java
@@ -50,6 +50,7 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
+
if (!supports(authentication.getClass())) {
return null;
}
@@ -99,10 +100,6 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
* @see org.acegisecurity.providers.AuthenticationProvider#supports(java.lang.Class)
*/
public boolean supports(Class authentication) {
- if (OpenIDAuthenticationToken.class.isAssignableFrom(authentication)) {
- return true;
- } else {
- return false;
- }
+ return OpenIDAuthenticationToken.class.isAssignableFrom(authentication);
}
}
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
index a28b2d7674..0506fb8ec1 100644
--- a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java
+++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDConsumer.java
@@ -22,41 +22,15 @@ import javax.servlet.http.HttpServletRequest;
/**
* An interface for OpenID library implementations
*
+ * @author Ray Krueger
* @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;
+ public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
+ throws OpenIDConsumerException;
- /**
- * DOCUMENT ME!
- *
- * @param req
- *
- * @return
- *
- * @throws OpenIDConsumerException
- */
public OpenIDAuthenticationToken endConsumption(HttpServletRequest req)
- throws OpenIDConsumerException;
+ throws OpenIDConsumerException;
- /**
- * DOCUMENT ME!
- *
- * @param returnToUrl
- */
- public void setReturnToUrl(String returnToUrl);
}
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
index eacf237e4c..65b620100e 100644
--- a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java
+++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIDLoginInitiationServlet.java
@@ -167,7 +167,7 @@ public class OpenIDLoginInitiationServlet extends HttpServlet {
} else {
// send the user the redirect url to proceed with OpenID authentication
try {
- String redirect = consumer.beginConsumption(req, id);
+ String redirect = consumer.beginConsumption(req, id, req.getRequestURL().toString());
logger.debug("Redirecting to: " + redirect);
res.sendRedirect(redirect);
} catch (OpenIDConsumerException oice) {
diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationProcessingFilter.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationProcessingFilter.java
new file mode 100644
index 0000000000..ffef5a212f
--- /dev/null
+++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationProcessingFilter.java
@@ -0,0 +1,186 @@
+/* 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.context.SecurityContextHolder;
+import org.acegisecurity.providers.openid.OpenIDAuthenticationToken;
+import org.acegisecurity.ui.AbstractProcessingFilter;
+import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$
+ * @version $Revision$
+ */
+public class OpenIdAuthenticationProcessingFilter extends AbstractProcessingFilter {
+ //~ Static fields/initializers =====================================================================================
+
+ private static final Log log = LogFactory.getLog(OpenIdAuthenticationProcessingFilter.class);
+ public static final String DEFAULT_CLAMED_IDENTITY_FIELD = "j_username";
+
+ //~ Instance fields ================================================================================================
+
+ private OpenIDConsumer consumer;
+ private String claimedIdentityFieldName = DEFAULT_CLAMED_IDENTITY_FIELD;
+ private String errorPage = "index.jsp";
+
+ //~ Methods ========================================================================================================
+
+ public Authentication attemptAuthentication(HttpServletRequest req)
+ throws AuthenticationException {
+ OpenIDAuthenticationToken token;
+
+ String identity = req.getParameter("openid.identity");
+
+ if (!StringUtils.hasText(identity)) {
+ throw new OpenIdAuthenticationRequiredException("External Authentication Required", obtainUsername(req));
+ }
+
+ 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;
+ }
+
+ protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
+ if (failed instanceof OpenIdAuthenticationRequiredException) {
+ OpenIdAuthenticationRequiredException openIdRequiredException = (OpenIdAuthenticationRequiredException) failed;
+ String claimedIdentity = openIdRequiredException.getClaimedIdentity();
+
+ if (StringUtils.hasText(claimedIdentity)) {
+ try {
+ String returnToUrl = buildReturnToUrl(request);
+ return consumer.beginConsumption(request, claimedIdentity, returnToUrl);
+ } catch (OpenIDConsumerException e) {
+ log.error("Unable to consume claimedIdentity [" + claimedIdentity + "]", e);
+ }
+ }
+ }
+
+ return super.determineFailureUrl(request, failed);
+ }
+
+ protected String buildReturnToUrl(HttpServletRequest request) {
+ return request.getRequestURL().toString();
+ }
+
+ public String getClaimedIdentityFieldName() {
+ return claimedIdentityFieldName;
+ }
+
+ public OpenIDConsumer getConsumer() {
+ return consumer;
+ }
+
+ public String getDefaultFilterProcessesUrl() {
+ return "/j_acegi_openid_security_check";
+ }
+
+ public String getErrorPage() {
+ return errorPage;
+ }
+
+ protected boolean isAuthenticated(HttpServletRequest request) {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+
+ return (auth != null) && auth.isAuthenticated();
+ }
+
+ /**
+ * The OpenIdAuthenticationProcessingFilter will ignore the request coming in if this method returns false.
+ * The default functionality checks if the request scheme starts with http.
This method should be overridden in subclasses that wish to consider a different strategy
+ *
+ * @param request HttpServletRequest we're processing
+ * @return true if this request is determined to be an OpenID request.
+ */
+ protected boolean isOpenIdRequest(HttpServletRequest request) {
+ String username = obtainUsername(request);
+ return (StringUtils.hasText(username)) && username.toLowerCase().startsWith("http");
+ }
+
+ protected String obtainUsername(HttpServletRequest req) {
+ return req.getParameter(claimedIdentityFieldName);
+ }
+
+ protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException failed) throws IOException {
+ if (failed instanceof OpenIdAuthenticationRequiredException) {
+ OpenIdAuthenticationRequiredException openIdAuthenticationRequiredException = (OpenIdAuthenticationRequiredException) failed;
+ request.setAttribute(OpenIdAuthenticationRequiredException.class.getName(),
+ openIdAuthenticationRequiredException.getClaimedIdentity());
+ }
+ }
+
+ public void setClaimedIdentityFieldName(String claimedIdentityFieldName) {
+ this.claimedIdentityFieldName = claimedIdentityFieldName;
+ }
+
+ public void setConsumer(OpenIDConsumer consumer) {
+ this.consumer = consumer;
+ }
+
+ public void setErrorPage(String errorPage) {
+ this.errorPage = errorPage;
+ }
+
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException failed) throws IOException {
+ SecurityContextHolder.getContext().setAuthentication(null);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Updated SecurityContextHolder to contain null Authentication");
+ }
+
+ String failureUrl = determineFailureUrl(request, failed);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Authentication request failed: " + failed.toString());
+ }
+
+ try {
+ request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
+ } catch (Exception ignored) {
+ }
+
+ super.getRememberMeServices().loginFail(request, response);
+
+ sendRedirect(request, response, failureUrl);
+ }
+}
diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationRequiredException.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationRequiredException.java
new file mode 100644
index 0000000000..315468057f
--- /dev/null
+++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/OpenIdAuthenticationRequiredException.java
@@ -0,0 +1,20 @@
+package org.acegisecurity.ui.openid;
+
+import org.acegisecurity.AuthenticationException;
+
+/**
+ * @author Ray Krueger
+ */
+public class OpenIdAuthenticationRequiredException extends AuthenticationException {
+
+ private final String claimedIdentity;
+
+ public OpenIdAuthenticationRequiredException(String msg, String claimedIdentity) {
+ super(msg);
+ this.claimedIdentity = claimedIdentity;
+ }
+
+ public String getClaimedIdentity() {
+ return claimedIdentity;
+ }
+}
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
index b275ab0095..8820cf26a8 100644
--- 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
@@ -66,7 +66,7 @@ public class JanRainOpenIDConsumer implements OpenIDConsumer, InitializingBean {
/* (non-Javadoc)
* @see org.acegisecurity.ui.openid.OpenIDConsumer#beginConsumption(java.lang.String)
*/
- public String beginConsumption(HttpServletRequest req, String identityUrl)
+ public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
throws OpenIDConsumerException {
// fetch/create a session Map for the consumer's use
HttpSession session = req.getSession();
@@ -103,7 +103,7 @@ public class JanRainOpenIDConsumer implements OpenIDConsumer, InitializingBean {
cp = cp.substring(1) + "/";
}
- String returnTo = trustRoot + cp + returnToUrl;
+ String returnTo = trustRoot + cp + this.returnToUrl;
// send the user the redirect url to proceed with OpenID authentication
return ar.redirectUrl(trustRoot, returnTo);
diff --git a/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/OpenId4JavaConsumer.java b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/OpenId4JavaConsumer.java
new file mode 100644
index 0000000000..e0194786ef
--- /dev/null
+++ b/sandbox/openid/src/main/java/org/acegisecurity/ui/openid/consumers/OpenId4JavaConsumer.java
@@ -0,0 +1,135 @@
+/* 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.OpenIDAuthenticationStatus;
+import org.acegisecurity.providers.openid.OpenIDAuthenticationToken;
+
+import org.acegisecurity.ui.openid.OpenIDConsumer;
+import org.acegisecurity.ui.openid.OpenIDConsumerException;
+
+import org.openid4java.association.AssociationException;
+
+import org.openid4java.consumer.ConsumerException;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.VerificationResult;
+
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.discovery.Identifier;
+
+import org.openid4java.message.AuthRequest;
+import org.openid4java.message.MessageException;
+import org.openid4java.message.ParameterList;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Ray Krueger
+ */
+public class OpenId4JavaConsumer implements OpenIDConsumer {
+ //~ Instance fields ================================================================================================
+
+ private final ConsumerManager consumerManager;
+
+ //~ Constructors ===================================================================================================
+
+ public OpenId4JavaConsumer(ConsumerManager consumerManager) {
+ this.consumerManager = consumerManager;
+ }
+
+ public OpenId4JavaConsumer() throws ConsumerException {
+ this(new ConsumerManager());
+ }
+
+ //~ Methods ========================================================================================================
+
+ public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
+ throws OpenIDConsumerException {
+ List discoveries;
+
+ try {
+ discoveries = consumerManager.discover(identityUrl);
+ } catch (DiscoveryException e) {
+ throw new OpenIDConsumerException("Error during discovery", e);
+ }
+
+ DiscoveryInformation information = consumerManager.associate(discoveries);
+ HttpSession session = req.getSession(true);
+ session.setAttribute(DiscoveryInformation.class.getName(), information);
+
+ AuthRequest authReq;
+
+ try {
+ authReq = consumerManager.authenticate(information, returnToUrl);
+ } catch (MessageException e) {
+ throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
+ } catch (ConsumerException e) {
+ throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
+ }
+
+ return authReq.getDestinationUrl(true);
+ }
+
+ public OpenIDAuthenticationToken endConsumption(HttpServletRequest request)
+ throws OpenIDConsumerException {
+ // extract the parameters from the authentication response
+ // (which comes in as a HTTP request from the OpenID provider)
+ ParameterList openidResp = new ParameterList(request.getParameterMap());
+
+ // retrieve the previously stored discovery information
+ DiscoveryInformation discovered = (DiscoveryInformation) request.getSession()
+ .getAttribute(DiscoveryInformation.class.getName());
+
+ // extract the receiving URL from the HTTP request
+ StringBuffer receivingURL = request.getRequestURL();
+ String queryString = request.getQueryString();
+
+ if ((queryString != null) && (queryString.length() > 0)) {
+ receivingURL.append("?").append(request.getQueryString());
+ }
+
+ // verify the response
+ VerificationResult verification;
+
+ try {
+ verification = consumerManager.verify(receivingURL.toString(), openidResp, discovered);
+ } catch (MessageException e) {
+ throw new OpenIDConsumerException("Error verifying openid response", e);
+ } catch (DiscoveryException e) {
+ throw new OpenIDConsumerException("Error verifying openid response", e);
+ } catch (AssociationException e) {
+ throw new OpenIDConsumerException("Error verifying openid response", e);
+ }
+
+ // examine the verification result and extract the verified identifier
+ Identifier verified = verification.getVerifiedId();
+
+ if (verified != null) {
+ return new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, verified.getIdentifier(),
+ "some message");
+ } else {
+ return new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE,
+ discovered.getClaimedIdentifier().getIdentifier(),
+ "Verification status message: [" + verification.getStatusMsg() + "]");
+ }
+ }
+}
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
index 616d63ae06..a0cd667b01 100644
--- 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
@@ -38,7 +38,7 @@ public class MockOpenIDConsumer implements OpenIDConsumer {
/* (non-Javadoc)
* @see org.acegisecurity.ui.openid.OpenIDConsumer#beginConsumption(javax.servlet.http.HttpServletRequest, java.lang.String)
*/
- public String beginConsumption(HttpServletRequest req, String identityUrl)
+ public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
throws OpenIDConsumerException {
return redirectUrl;
}