blob: a258759c473838fccf16b5243f89c2eb0a7a696a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.geronimo.components.jaspi.modules.openid;
import java.util.Map;
import java.util.List;
import java.io.IOException;
import javax.security.auth.message.module.ServerAuthModule;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.auth.message.callback.GroupPrincipalCallback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.Subject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.InMemoryConsumerAssociationStore;
import org.openid4java.consumer.InMemoryNonceVerifier;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.MessageException;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ParameterList;
/**
* @version $Rev:$ $Date:$
*/
public class OpenIDServerAuthModule implements ServerAuthModule {
private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[] {HttpServletRequest.class, HttpServletResponse.class};
public static final String MANDATORY_KEY = "javax.security.auth.message.MessagePolicy.isMandatory";
public static final String AUTH_METHOD_KEY = "javax.servlet.http.authType";
private static final String OPENID_IDENTIFIER = "openid_identifier";
private static final String DISCOVERY_SESSION_KEY = "openid-disc";
private static final String RETURN_ADDRESS = "/_openid_security_check";
private static final String ORIGINAL_URI_KEY = "org.apache.geronimo.components.jaspi.openid.URI";
private CallbackHandler callbackHandler;
private ConsumerManager consumerManager;
private static final String ID_KEY = "org.apache.geronimo.components.jaspi.openid.ID";
public Class[] getSupportedMessageTypes() {
return SUPPORTED_MESSAGE_TYPES;
}
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException {
this.callbackHandler = callbackHandler;
try {
consumerManager = new ConsumerManager();
} catch (ConsumerException e) {
throw (AuthException)new AuthException("Unable to create ConsumerManager").initCause(e);
}
consumerManager.setAssociations(new InMemoryConsumerAssociationStore());
consumerManager.setNonceVerifier(new InMemoryNonceVerifier(5000));
//??
consumerManager.getRealmVerifier().setEnforceRpId(false);
}
public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
}
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {
HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
HttpSession session = request.getSession(isMandatory(messageInfo));
//auth not mandatory and not logged in.
if (session == null) {
return AuthStatus.SUCCESS;
}
String uri = request.getPathInfo();
//are we returning from the OP redirect?
if (uri.endsWith(RETURN_ADDRESS)) {
ParameterList parameterList = new ParameterList(request.getParameterMap());
DiscoveryInformation discovered = (DiscoveryInformation) session.getAttribute(DISCOVERY_SESSION_KEY);
//TODO what if its missing?
String originalURI = (String) session.getAttribute(ORIGINAL_URI_KEY);
try {
//TODO is originalURI correct for verify call???
VerificationResult verification = consumerManager.verify(originalURI, parameterList, discovered);
Identifier identifier = verification.getVerifiedId();
session.setAttribute(ID_KEY, identifier);
//redirect back to original page
session.removeAttribute(ORIGINAL_URI_KEY);
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(originalURI));
return AuthStatus.SEND_CONTINUE;
// } catch (MessageException e) {
//
// } catch (DiscoveryException e) {
//
// } catch (AssociationException e) {
//
// } catch (IOException e) {
} catch (Exception e) {
try {
//TODO redirect to error page or just send error
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
} catch (IOException e1) {
}
}
return AuthStatus.SEND_FAILURE;
}
//are we already logged in, and not expired?
Identifier identifier = (Identifier) session.getAttribute(ID_KEY);
if (identifier != null) {
//TODO set up subject and callback handler.
final IdentifierPrincipal principal = new IdentifierPrincipal(identifier.getIdentifier());
clientSubject.getPrincipals().add(principal);
clientSubject.getPrincipals().add(new AuthenticatedPrincipal());
CallerPrincipalCallback cpCallback = new CallerPrincipalCallback(clientSubject, principal);
GroupPrincipalCallback gpCallback = new GroupPrincipalCallback(clientSubject, new String[] {"authenticated"});
try {
callbackHandler.handle(new Callback[] {cpCallback, gpCallback});
} catch (IOException e) {
} catch (UnsupportedCallbackException e) {
}
return AuthStatus.SUCCESS;
}
//assume not...
String openidIdentifier = request.getParameter(OPENID_IDENTIFIER);
try {
List<DiscoveryInformation> discoveries = consumerManager.discover(openidIdentifier);
//associate with one OP
DiscoveryInformation discovered = consumerManager.associate(discoveries);
//save association info in session
session.setAttribute(DISCOVERY_SESSION_KEY, discovered);
AuthRequest authRequest = consumerManager.authenticate(discovered, RETURN_ADDRESS);
//save original uri in response, to be retrieved after redirect returns
session.setAttribute(ORIGINAL_URI_KEY, getFullRequestURI(request).toString());
//TODO openid 2.0 form redirect
response.sendRedirect(authRequest.getDestinationUrl(true));
return AuthStatus.SEND_CONTINUE;
} catch (DiscoveryException e) {
throw (AuthException) new AuthException("Could not authenticate").initCause(e);
} catch (ConsumerException e) {
throw (AuthException) new AuthException("Could not authenticate").initCause(e);
} catch (MessageException e) {
throw (AuthException) new AuthException("Could not authenticate").initCause(e);
} catch (IOException e) {
throw (AuthException) new AuthException("Could not authenticate").initCause(e);
}
// return null;
}
private StringBuilder getFullRequestURI(HttpServletRequest request) {
StringBuilder builder = new StringBuilder();
builder.append(request.getScheme()).append("://");
builder.append(request.getServerName()).append(":");
builder.append(request.getServerPort());
//TODO jetty combines this with the uri and query string. Can this have query params?
builder.append(request.getContextPath());
builder.append(request.getPathInfo());
if (request.getQueryString() != null && request.getQueryString().length() > 0) {
builder.append("?").append(request.getQueryString());
}
return builder;
}
private boolean isMandatory(MessageInfo messageInfo) {
String mandatory = (String) messageInfo.getMap().get(MANDATORY_KEY);
if (mandatory == null){
return false;
}
return Boolean.valueOf(mandatory);
}
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException {
return AuthStatus.SUCCESS;
}
}