/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.user;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.joda.beans.impl.flexi.FlexiBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.util.auth.AuthUtils;
import com.opengamma.web.AbstractSingletonWebResource;
import com.opengamma.web.WebHomeResource;
/**
* RESTful resource for the login page.
*/
@Path("/login")
public class WebLoginResource extends AbstractSingletonWebResource {
// take control of logout from Shiro to enable ftl files
/**
* OpenGamma specific header for client IP address.
*/
private static final String HEADER_X_OPENGAMMA_CLIENT_IP = "X-OPENGAMMA-CLIENT-IP";
/**
* General forwarded header for finding IP address.
*/
private static final String HEADER_X_FORWARDED_FOR = "X-FORWARDED-FOR";
/** Logger. */
private static final Logger s_logger = LoggerFactory.getLogger(WebLoginResource.class);
// one resource class handles two ftl files
private static final String LOGIN_GREEN = "users/html/login.ftl";
private static final String LOGIN_STYLISH = "users/html/login-og.ftl";
// Key for the login user name
static final Object LOGIN_USERNAME = WebLoginResource.class.getName() + ".LoginUserName";
/**
* Creates the resource.
*/
public WebLoginResource() {
}
//-------------------------------------------------------------------------
@GET
@Produces(MediaType.TEXT_HTML)
public String getGreen(
@Context HttpServletRequest request,
@Context ServletContext servletContext,
@Context UriInfo uriInfo) {
SavedRequest savedRequest = WebUtils.getSavedRequest(request);
if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
if (savedRequest.getRequestUrl() != null && savedRequest.getRequestUrl().contains("/bundles/fm/prototype/")) {
return getStylish(servletContext, uriInfo);
}
}
return get(servletContext, uriInfo, LOGIN_GREEN);
}
@GET
@Path("og")
@Produces(MediaType.TEXT_HTML)
public String getStylish(
@Context ServletContext servletContext,
@Context UriInfo uriInfo) {
return get(servletContext, uriInfo, LOGIN_STYLISH);
}
private String get(ServletContext servletContext, UriInfo uriInfo, String ftlFile) {
FlexiBean out = createRootData(uriInfo);
Subject subject = AuthUtils.getSubject();
Session session = subject.getSession(false);
if (session != null && session.getAttribute(LOGIN_USERNAME) != null) {
out.put("username", session.getAttribute(LOGIN_USERNAME));
} else {
out.put("username", "");
}
return getFreemarker(servletContext).build(ftlFile, out);
}
//-------------------------------------------------------------------------
@POST
@Produces(MediaType.TEXT_HTML)
public Response loginGreen(
@Context HttpServletRequest request,
@Context ServletContext servletContext,
@Context UriInfo uriInfo,
@FormParam("username") String username,
@FormParam("password") String password) {
return login(request, servletContext, uriInfo, username, password, LOGIN_GREEN);
}
@POST
@Path("og")
@Produces(MediaType.TEXT_HTML)
public Response loginStylish(
@Context HttpServletRequest request,
@Context ServletContext servletContext,
@Context UriInfo uriInfo,
@FormParam("username") String username,
@FormParam("password") String password) {
return login(request, servletContext, uriInfo, username, password, LOGIN_STYLISH);
}
private Response login(
HttpServletRequest request,
ServletContext servletContext,
UriInfo uriInfo,
String username,
String password,
String ftlFile) {
username = StringUtils.trimToNull(username);
password = StringUtils.trimToNull(password);
if (username == null) {
return displayError(servletContext, uriInfo, username, ftlFile, "UserNameMissing");
}
if (password == null) {
return displayError(servletContext, uriInfo, username, ftlFile, "PasswordMissing");
}
String ipAddress = findIpAddress(request);
UsernamePasswordToken token = new UsernamePasswordToken(username, password, false, ipAddress);
try {
Subject subject = AuthUtils.getSubject();
subject.login(token);
token.clear();
URI successUrl = null;
SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
successUrl = uriInfo.getBaseUri().resolve(savedRequest.getRequestUrl());
} else {
if (ftlFile.equals(LOGIN_GREEN)) {
successUrl = WebHomeResource.uri(uriInfo);
} else {
successUrl = uriInfo.getBaseUri().resolve("/");
}
}
return Response.seeOther(successUrl).build();
} catch (AuthenticationException ex) {
String errorCode = StringUtils.substringBeforeLast(ex.getClass().getSimpleName(), "Exception");
return displayError(servletContext, uriInfo, username, ftlFile, errorCode);
}
}
/**
* Finds the IP address of the user.
* <p>
* This is a generally impossible task.
* We prefer a specific 'X_OPENGAMMA_CLIENT_IP' header containing a single IP address.
* If not found, we rely on the generic 'X-FORWARDED-FOR' header.
* If not found, we rely on the remote host of the servlet request.
*
* @param request the servlet request, not null
* @return the IP address, not null
*/
private static String findIpAddress(HttpServletRequest request) {
String remoteIp = StringUtils.stripToNull(request.getHeader(HEADER_X_OPENGAMMA_CLIENT_IP));
if (remoteIp == null) {
remoteIp = StringUtils.stripToNull(request.getHeader(HEADER_X_FORWARDED_FOR));
if (remoteIp != null) {
remoteIp = StringUtils.stripToNull(StringUtils.split(remoteIp, ',')[0]);
if ("unknown".equalsIgnoreCase(remoteIp)) {
remoteIp = null;
}
}
}
if (remoteIp == null) {
remoteIp = request.getRemoteHost();
}
return ensureIpAddressNonLoopback(remoteIp);
}
/**
* Ensures that the IP address is non-local.
*
* @param remoteIp the remote IP address, may be null
* @return the non-local IP address, not null
*/
private static String ensureIpAddressNonLoopback(String remoteIp) {
try {
InetAddress ia = (remoteIp != null ? InetAddress.getByName(remoteIp) : null);
if (ia != null && ia.isLoopbackAddress() == false) {
return remoteIp;
}
// search through network interfaces to find reasonable non-loopback IP address
InetAddress possible = null;
for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) {
NetworkInterface iface = ifaces.nextElement();
for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
InetAddress inetAddr = inetAddrs.nextElement();
if (inetAddr.isLoopbackAddress() == false) {
if (inetAddr.isSiteLocalAddress()) {
return inetAddr.getHostAddress();
} else if (possible == null) {
possible = inetAddr;
}
}
}
}
if (possible != null) {
return possible.getHostAddress();
}
InetAddress localHost = InetAddress.getLocalHost();
if (localHost == null) {
throw new UnknownHostException("Unknown local host");
}
return localHost.getHostAddress();
} catch (Exception ex) {
s_logger.warn("Unable to obtain suitable local host address", ex);
return (remoteIp != null ? remoteIp : "0:0:0:0:0:0:0:1");
}
}
private Response displayError(ServletContext servletContext, UriInfo uriInfo, String username, String ftlFile, String errorCode) {
FlexiBean out = createRootData(uriInfo);
out.put("username", username);
out.put("err_invalidLogin", errorCode);
return Response.ok(getFreemarker(servletContext).build(ftlFile, out)).build();
}
//-------------------------------------------------------------------------
/**
* Builds a URI for this page.
*
* @param uriInfo the uriInfo, not null
* @return the URI, not null
*/
public static URI uri(UriInfo uriInfo) {
return uriInfo.getBaseUriBuilder().path(WebLoginResource.class).build();
}
}