package org.verisign.joid.server;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONObject;
import org.verisign.joid.AuthenticationRequest;
import org.verisign.joid.Crypto;
import org.verisign.joid.OpenId;
import org.verisign.joid.OpenIdException;
import org.verisign.joid.RequestFactory;
import org.verisign.joid.ServerInfo;
import org.verisign.joid.Store;
import org.verisign.joid.StoreFactory;
import org.verisign.joid.util.CookieUtils;
import org.verisign.joid.util.DependencyUtils;
import edu.stanford.prpl.junction.api.activity.ActivityDescription;
import edu.stanford.prpl.junction.impl.JunctionMaker;
import edu.stanford.prpl.phoneIdp.common.PhoneIdpCommon;
import edu.stanford.prpl.phoneIdp.server.api.HttpPhoneIdpManager;
import edu.stanford.prpl.phoneIdp.server.api.PhoneIdp;
import edu.stanford.prpl.phoneIdp.server.impl.HttpPhoneIdpManagerImpl;
import edu.stanford.prpl.phoneIdp.server.impl.PhoneIdpImpl;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Map;
/**
* User: treeder
* Date: Jul 18, 2007
* Time: 4:50:33 PM
*/
public class OpenIdServlet extends HttpServlet
{
private static Log log = LogFactory.getLog(OpenIdServlet.class);
private static final long serialVersionUID = 297366254782L;
private static OpenId openId;
private Store store;
private Crypto crypto;
private String loginPage;
public static final String USERNAME_ATTRIBUTE = "username";
public static final String ID_CLAIMED = "idClaimed";
public static final String QUERY = "query";
public static final String COOKIE_AUTH_NAME = "authKey";
public static final String COOKIE_USERNAME = "username";
private static UserManager userManager;
//DSG
private HttpPhoneIdpManager phoneIdpManager_;
public static final String SHARED_SECRET = "sharedSecret";
public static final String OPENID = "openid.claimed_id";
public static final String CHALLENGE = "challenge";
public static final String RESPONSE = "response";
public static final String VERIFY_RESULT = "verifyresult";
public static final String AUTHCODE = "authcode";
public static final String MODE = "mode";
public static final String PIDPVERIFIED_ATTRIBUTE = "pidpverified";
public void init(ServletConfig config) throws ServletException
{
super.init(config);
String storeClassName = config.getInitParameter("storeClassName");
String userManagerClassName = config.getInitParameter("userManagerClassName");
store = StoreFactory.getInstance(storeClassName);
MemoryStore mStore = (MemoryStore) store;
mStore.setAssociationLifetime(600);
userManager = (UserManager) DependencyUtils.newInstance(userManagerClassName);
crypto = new Crypto();
loginPage = config.getInitParameter("loginPage");
String endPointUrl = config.getInitParameter("endPointUrl");
openId = new OpenId(new ServerInfo(endPointUrl, store, crypto));
//dsg
phoneIdpManager_ = HttpPhoneIdpManagerImpl.getInstance();
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
String mode = request.getParameter(PhoneIdpCommon.REQUEST_MODE);
if (mode != null)
{
if (mode.equalsIgnoreCase(PhoneIdpCommon.CREATE_CHALLENGE_MODE))
{
phoneIdpManager_.createChallenge(request, response);
}
else if (mode.equalsIgnoreCase(PhoneIdpCommon.VERIFY_RESPONSE_MODE))
{
phoneIdpManager_.verifyResponse(request, response);
}
else if (mode.equalsIgnoreCase(PhoneIdpCommon.IS_VERIFIED_MODE))
{
phoneIdpManager_.isVerified(request, response);
}
}
else
{
doQuery(request.getQueryString(), request, response);
}
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
StringBuffer sb = new StringBuffer();
Enumeration e = request.getParameterNames();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
String[] values = request.getParameterValues(name);
if (values.length == 0) {
throw new IOException("Empty value not allowed: "
+ name + " has no value");
}
try {
sb.append(URLEncoder.encode(name, "UTF-8") + "="
+ URLEncoder.encode(values[0], "UTF-8"));
} catch (UnsupportedEncodingException ex) {
throw new IOException(ex.toString());
}
if (e.hasMoreElements()) {
sb.append("&");
}
}
doQuery(sb.toString(), request, response);
}
public void doQuery(String query,
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
log("\nrequest\n-------\n" + query + "\n");
if (!(openId.canHandle(query))) {
returnError(query, response);
return;
}
try {
boolean isAuth = openId.isAuthenticationRequest(query);
HttpSession session = request.getSession(true);
String user = getLoggedIn(request);
log.debug("[OpenIdServlet] Logged in as: " + user);
if (request.getParameter(AuthenticationRequest.OPENID_TRUST_ROOT ) != null){
session.setAttribute (
AuthenticationRequest.OPENID_TRUST_ROOT,
request.getParameter(AuthenticationRequest.OPENID_TRUST_ROOT));
}
if (request.getParameter(AuthenticationRequest.OPENID_RETURN_TO ) != null){
session.setAttribute(
AuthenticationRequest.OPENID_RETURN_TO,
request.getParameter(AuthenticationRequest.OPENID_RETURN_TO));
}
if (isAuth && user == null) {
// todo: should ask user to accept realm even if logged in, but only once
// ask user to accept this realm
RequestDispatcher rd = request.getRequestDispatcher(loginPage);
request.setAttribute(QUERY, query);
request.setAttribute(AuthenticationRequest.OPENID_REALM, request.getParameter(AuthenticationRequest.OPENID_REALM));
session.setAttribute(QUERY, query);
//if claimed_id is null then use identity instead (because of diffs between v2 & v1 of spec)
if ( request.getParameter(AuthenticationRequest.OPENID_CLAIMED_ID) == null){
session.setAttribute(
AuthenticationRequest.OPENID_CLAIMED_ID,
request.getParameter (AuthenticationRequest.OPENID_IDENTITY));
} else {
session.setAttribute(
AuthenticationRequest.OPENID_CLAIMED_ID,
request.getParameter (AuthenticationRequest.OPENID_CLAIMED_ID));
}
session.setAttribute(
AuthenticationRequest.OPENID_REALM,
request.getParameter(AuthenticationRequest.OPENID_REALM));
// rd.forward(request, response);
response.sendRedirect(loginPage);
return;
}
String s = openId.handleRequest(query);
log("\nresponse\n--------\n" + s + "\n");
if (isAuth) {
//// DSG
/*
HttpPhoneIdpManagerImpl httpPhoneIdpManager_ = HttpPhoneIdpManagerImpl.getInstance();
httpPhoneIdpManager_.getUpdateAccountDetails(request, response);
*/
AuthenticationRequest authReq = (AuthenticationRequest)
RequestFactory.parse(query);
// String claimedId = (String) session.getAttribute(ID_CLAIMED);
/* Ensure that the previously claimed id is the same as the just
passed in claimed id. */
String identity;
if ( request.getParameter(AuthenticationRequest.OPENID_CLAIMED_ID) == null){
identity = request.getParameter(AuthenticationRequest.OPENID_IDENTITY);
} else {
identity = authReq.getClaimedIdentity();
}
//if (getUserManager().canClaim(user, authReq.getClaimedIdentity())) {
if (true) {
//String returnTo = authReq.getReturnTo();
String returnTo = (String) session.getAttribute(AuthenticationRequest.OPENID_RETURN_TO );
String delim = (returnTo.indexOf('?') >= 0) ? "&" : "?";
s = response.encodeRedirectURL(returnTo + delim + s);
log.debug("sending redirect to: " + s);
response.sendRedirect(s);
} else {
throw new OpenIdException("User cannot claim this id.");
}
} else {
// Association request
int len = s.length();
PrintWriter out = response.getWriter();
response.setHeader("Content-Length", Integer.toString(len));
if (openId.isAnErrorResponse(s)) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
out.print(s);
out.flush();
}
} catch (OpenIdException e) {
e.printStackTrace();
response.sendError(HttpServletResponse
.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
}
/**
*
* @param request
* @return Username the user is logged in as
*/
public static String getLoggedIn(HttpServletRequest request)
{
String o = (String) request.getSession(true).getAttribute(USERNAME_ATTRIBUTE);
if (o != null) return o;
// check Remember Me cookies
String authKey = CookieUtils.getCookieValue(request, COOKIE_AUTH_NAME, null);
if (authKey != null) {
String username = CookieUtils.getCookieValue(request, COOKIE_USERNAME, null);
if (username != null) {
// lets check the UserManager to make sure this is a valid match
o = getUserManager().getRememberedUser(username, authKey);
if (o != null) {
request.getSession(true).setAttribute(USERNAME_ATTRIBUTE, o);
}
}
}
return o;
}
/**
*
* @param request
* @param username if null, will logout
*/
public static void setLoggedIn(HttpServletRequest request, String username){
request.getSession(true).setAttribute(USERNAME_ATTRIBUTE, username);
}
private void returnError(String query, HttpServletResponse response)
throws ServletException, IOException
{
Map map = RequestFactory.parseQuery(query);
String returnTo = (String) map.get("openid.return_to");
boolean goodReturnTo = false;
try {
URL url = new URL(returnTo);
goodReturnTo = true;
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (goodReturnTo) {
String s = "?openid.ns:http://specs.openid.net/auth/2.0"
+ "&openid.mode=error&openid.error=BAD_REQUEST";
s = response.encodeRedirectURL(returnTo + s);
response.sendRedirect(s);
} else {
PrintWriter out = response.getWriter();
// response.setContentLength() seems to be broken,
// so set the header manually
String s = "ns:http://specs.openid.net/auth/2.0\n"
+ "&mode:error"
+ "&error:BAD_REQUEST\n";
int len = s.length();
response.setHeader("Content-Length", Integer.toString(len));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
out.print(s);
out.flush();
}
}
public void log(String s)
{
// todo: resolve issue with non-prime servlet container + log4j/commons and replace
System.out.println(s);
}
/**
* This sets a session variable stating that the claimed_id for this request
* has been verified so we can now return back to the relying party.
*
* @param session
* @param claimedId
*/
public static void idClaimed(HttpSession session, String claimedId)
{
session.setAttribute(ID_CLAIMED, claimedId);
}
public static UserManager getUserManager()
{
if (null == userManager) {
System.out.println("Warning: creating new userManager object");
userManager = new MemoryUserManager();
}
return userManager;
}
public static ProviderActor getProviderActorInstance(HttpServletRequest request) {
ProviderActor prov = new ProviderActor(request); // need to send IdP manager
ActivityDescription desc = new ActivityDescription();
desc.setActivityID("prpl.openid.auth");
desc.setFriendlyName("OpenID Auth");
// not sure if this is right :) will fix later.
JSONObject mobilePlatform = new JSONObject();
try {
mobilePlatform.put("platform", "android");
mobilePlatform.put("url","http://path/to/my.apk");
} catch (Exception e) {}
desc.addRolePlatform("authenticator", mobilePlatform);
// TODO: set download location of mobile code
JunctionMaker.getInstance("prpl.stanford.edu").newJunction(desc, prov);
// store the jx session as our random data to be validated:
ProviderActor.setSessionData(request, prov.getJunction().getSessionID());
return prov;
// probably put this in a map somewhere?
}
}