/* *****************************************************************************
* AuthenticationServlet.java
* ****************************************************************************/
/* J_LZ_COPYRIGHT_BEGIN *******************************************************
* Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* J_LZ_COPYRIGHT_END *********************************************************/
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletConfig.*;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
public class AuthenticationServlet extends HttpServlet
{
//----------------------------------------------------------------------
// Parameters
//----------------------------------------------------------------------
public static final String PARAM_REQUEST = "rt";
public static final String PARAM_USER = "usr";
public static final String PARAM_PASSWORD = "pwd";
//----------------------------------------------------------------------
// Request types
//----------------------------------------------------------------------
public static final String REQ_UNKNOWN = "unknown";
public static final String REQ_LOGIN = "login";
public static final String REQ_LOGINGUEST = "loginguest";
public static final String REQ_LOGOUT = "logout";
public static final String REQ_GETUSERNAME = "getusername";
//----------------------------------------------------------------------
// Status code types
//----------------------------------------------------------------------
public static final int STAT_OK = 0;
public static final int STAT_UNKNOWN = 1;
public static final int STAT_ERROR = 2;
public static final int STAT_INVALID = 3;
public static final int STAT_INVALID_SESSION = 4;
public static final int STAT_FORBIDDEN = 5;
//----------------------------------------------------------------------
// XML tag names
//----------------------------------------------------------------------
public static final String TAG_ROOT = "authentication";
public static final String TAG_STATUS = "status";
public static final String TAG_RESPONSE = "response";
public static final String TAG_USER = "user";
public static final String TAG_USERNAME = "username";
/** Static counter for guest name. */
private static int sGuestCounter = 0;
private static Object sLock = new Object();
/** Attribute session for username. */
static public String ATTR_USERNAME = "username";
/** Default authentication initialization file; this file is assumed to
* exist in the WEB-INF directory. */
private String DEFAULT_USERS_FILE = "lzusers.xml";
/** Map to store user information */
private HashMap mUserMap = new HashMap();
/** Flag to allow get guest interface. */
private boolean mAllowLoginGuest = true;
/** Logger */
private static Logger mLogger = Red5LoggerFactory.getLogger(AuthenticationServlet.class, ScopeApplicationAdapter.webAppRootKey);
/**
* Initialize servlet.
* @param config configuration file object
*/
public void init(ServletConfig config)
throws ServletException
{
super.init (config);
log("Initializing AuthenticationServlet!");
ServletContext ctxt = config.getServletContext();
// Sanity check the servlet context version
if (ctxt.getMajorVersion() < 2 || ctxt.getMinorVersion() < 2)
throw new ServletException("must be at least servlet 2.2");
//------------------------------------------------------------
// Create internal name, password table
//------------------------------------------------------------
// Figure out where lps config directory is.
String ctxtPath = ctxt.getRealPath("/");
String delim =
(ctxtPath.lastIndexOf(File.separator) == ctxtPath.length()-1 ? "" : File.separator);
String lpsConfigDir = getInitParameter("lps.config.dir");
if (lpsConfigDir == null) {
lpsConfigDir = System.getProperty("lps.config.dir");
if (lpsConfigDir == null) {
lpsConfigDir = "config";
}
}
String configDir = ctxtPath + delim + "WEB-INF"
+ File.separator + "lps"
+ File.separator + lpsConfigDir;
// Get connection parameter.
String usersFile = config.getInitParameter("LZ_USERS_FILE");
if (usersFile == null)
usersFile = DEFAULT_USERS_FILE;
String fullPath = getFullPath(configDir, usersFile);
try {
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(new File(fullPath));
Element root = document.getRootElement();
List userList = root.getChildren(TAG_USER);
// Create user information map.
int userListLen = userList.size();
for (int i = 0; i < userListLen; i++) {
Element userTag = (Element)userList.get(i);
String name = userTag.getAttributeValue("name");
String password = userTag.getAttributeValue("password");
if (name!=null && password!=null)
mUserMap.put(name, password);
}
} catch (IOException e) {
String info = "init exception: " + e.getMessage();
mLogger.debug(info);
throw new ServletException(info);
} catch (JDOMException e) {
String info = "init exception: " + e.getMessage();
mLogger.debug(info);
throw new ServletException(info);
}
//------------------------------------------------------------
// Flag to allow guest naming interface.
//------------------------------------------------------------
String loginGuestStr = config.getInitParameter("ALLOW_GUEST_LOGIN");
if (loginGuestStr!=null)
mAllowLoginGuest = loginGuestStr.equals("true");
}
/**
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
mLogger.debug("doGet(req, res)");
mLogger.debug("Request: uri " + req.getRequestURI());
mLogger.debug("Request: query string " + req.getQueryString());
Enumeration headers = req.getHeaderNames();
while(headers.hasMoreElements()) {
String h = (String)headers.nextElement();
mLogger.debug(" Header: " + h + " : " + req.getHeader(h));
}
Element root = new Element(TAG_ROOT);
// Request type
String rt = req.getParameter(PARAM_REQUEST);
boolean isOk = false;
if (rt == null)
doUnknown(req, root);
else if (rt.equals(REQ_LOGIN))
doLogin(req, res, root);
else if (rt.equals(REQ_LOGINGUEST))
if (mAllowLoginGuest)
doLoginGuest(req, res, root);
else
doForbidden(req, rt, root);
else if (rt.equals(REQ_LOGOUT))
doLogout(req, res, root);
else if (rt.equals(REQ_GETUSERNAME))
doGetUsername(req, root);
else
doUnknown(req, root);
res.setHeader ("Pragma", "no-cache");
res.setHeader ("Cache-Control", "no-cache");
String xml = new XMLOutputter().outputString(new Document(root));
ServletOutputStream out = res.getOutputStream();
out.println(xml);
}
/**
* Handle post request.
*
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
mLogger.debug("doPost(req, res)");
doGet(req, res);
}
/**
* Login user and create session.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doLogin(HttpServletRequest req, HttpServletResponse res,
Element root)
{
mLogger.debug("doLogin(req, res, root)");
String username = req.getParameter(PARAM_USER);
if ( username != null && username.equals("guest") ) {
if (mAllowLoginGuest)
doLoginGuest(req, res, root);
else
doForbidden(req, REQ_LOGIN, root);
return;
}
int code = STAT_INVALID;
Element eRequest = getRequestElement(REQ_LOGIN);
Element eUsername = null;
String password = req.getParameter(PARAM_PASSWORD);
if ( username != null && ! username.equals("") &&
password != null && ! password.equals("") ) {
String checkPassword = (String)mUserMap.get(username);
if (checkPassword != null && !checkPassword.equals("")) {
if (password.equals(checkPassword)) {
HttpSession sess = req.getSession();
sess.setAttribute(ATTR_USERNAME, username);
eUsername = new Element(TAG_USERNAME)
.addContent(username);
code = STAT_OK;
}
}
}
Element eStatus = getStatusElement(code);
eRequest.addContent(eStatus);
if (eUsername != null)
eRequest.addContent(eUsername);
root.addContent(eRequest);
}
/**
* Fetch a guest name.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doLoginGuest(HttpServletRequest req, HttpServletResponse res,
Element root)
{
mLogger.debug("doLoginGuest(req, res, root)");
String guest;
synchronized (sLock) {
guest = "guest" + sGuestCounter++;
}
HttpSession sess = req.getSession();
sess.setAttribute(ATTR_USERNAME, guest);
Element eRequest = getRequestElement(REQ_LOGINGUEST);
eRequest.addContent(getStatusElement(STAT_OK));
Element eUsername = new Element(TAG_USERNAME)
.addContent(guest);
eRequest.addContent(eUsername);
root.addContent(eRequest);
}
/**
* Remove session.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doLogout(HttpServletRequest req, HttpServletResponse res,
Element root)
{
mLogger.debug("doLogout(req, res, root)");
Element eRequest = getRequestElement(REQ_LOGOUT);
HttpSession sess = req.getSession();
if (sess.getAttribute(ATTR_USERNAME) != null) {
sess.invalidate();
eRequest.addContent(getStatusElement(STAT_OK));
} else {
eRequest.addContent(getStatusElement(STAT_INVALID_SESSION));
}
root.addContent(eRequest);
}
/**
* Get username of current session, if any.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doGetUsername(HttpServletRequest req , Element root)
{
mLogger.debug("doGetUsername(req, root)");
Element eRequest = getRequestElement(REQ_GETUSERNAME);
HttpSession sess = req.getSession();
// Session is only valid if we have a username
String username = (String)sess.getAttribute(ATTR_USERNAME);
if (username != null) {
eRequest.addContent(getStatusElement(STAT_OK));
Element eUsername = new Element(TAG_USERNAME)
.addContent(username);
eRequest.addContent(eUsername);
} else {
eRequest.addContent(getStatusElement(STAT_INVALID_SESSION));
}
root.addContent(eRequest);
}
/**
* Return unknown response.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doUnknown(HttpServletRequest req, Element root)
{
mLogger.debug("doUnknown(req, root)");
Element eRequest = getRequestElement(REQ_UNKNOWN);
eRequest.addContent(getStatusElement(STAT_UNKNOWN));
root.addContent(eRequest);
}
/**
* Return forbidden response.
* @param req @see HttpServletRequest
* @param res @see HttpServletResponse
*/
private void doForbidden(HttpServletRequest req, String rt, Element root)
{
mLogger.debug("doForbidden(req, root)");
Element eRequest = getRequestElement(rt);
eRequest.addContent(getStatusElement(STAT_FORBIDDEN));
root.addContent(eRequest);
}
/**
* Get a status element with a particular message attached.
* @param code code of status
* @param message status message
* @return an element with status information
*/
private Element getStatusElement(int code, String msg)
{
mLogger.debug("getStatusElement(code=" + code + ", msg=" + msg + ")");
return new Element(TAG_STATUS)
.setAttribute("code", Integer.toString(code))
.setAttribute("msg", msg);
}
/**
* Get a status element.
* @param code code of status
* @return an element with status information
*/
private Element getStatusElement(int code)
{
mLogger.debug("getStatusElement(code=" + code + ")");
return getStatusElement(code, getStatusCodeMessage(code));
}
/**
* Get a request element.
* @param type type of request
* @return an element with request information
*/
private Element getRequestElement(String type)
{
mLogger.debug("getRequestElement(type=" + type + ")");
return new Element(TAG_RESPONSE)
.setAttribute("type", type);
}
/**
* Get status code of message.
* @param code integer code
* @return string representation of code
*/
private String getStatusCodeMessage(int code)
{
mLogger.debug("getStatusCodeMessage(code=" + code + ")");
if (code == STAT_INVALID)
return "invalid";
if (code == STAT_ERROR)
return "error";
if (code == STAT_OK)
return "ok";
if (code == STAT_INVALID_SESSION)
return "invalid session";
if (code == STAT_FORBIDDEN)
return "forbidden";
return "unknown"; // STAT_UNKNOWN
}
/**
* Create a full path based by appending file to dir. File has to be of
* relative path or null is returned.
* @param dir directory name
* @param file file name
* @return string concatenated with dir and file or null if file is of
* absolute path
*/
static public String getFullPath(String dir, String file)
{
mLogger.debug("getFullPath(dir=" + dir + ", file=" + file + ")");
final String fileSeparator = System.getProperty("file.separator");
String fullPath = "";
if (dir != null && dir.length() != 0) {
fullPath = dir;
boolean dirHasLastSeparator =
dir.lastIndexOf(fileSeparator) == dir.length()-1;
if (! dirHasLastSeparator)
fullPath += fileSeparator;
}
fullPath += file;
return fullPath;
}
/**
* Replace real path forward slash characters to back-slash for Windows.
* This is to get around a WebSphere problem where calling getRealPath()
* returns path with mixed file separators.
* @param ctxt servlet context
* @param path virtual webapp path to resolve into a real path
* @return the real path, or null if the translation cannot be performed
*/
public String getRealPath(ServletContext ctxt, String path)
{
String realPath = ctxt.getRealPath(path);
if ( realPath != null && File.separatorChar == '\\' )
realPath = realPath.replace('/', '\\');
return realPath;
}
}