/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.webdav;
import com.concursive.commons.codec.PasswordHash;
import com.concursive.commons.db.DatabaseUtils;
import com.concursive.connect.web.webdav.beans.WebdavUser;
import com.concursive.connect.web.webdav.context.BaseWebdavContext;
import com.concursive.connect.web.webdav.context.ModuleContext;
import com.concursive.connect.web.webdav.dao.WebdavModule;
import com.concursive.connect.web.webdav.dao.WebdavModuleList;
import com.concursive.connect.web.webdav.servlets.WebdavServlet;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.util.MD5Encoder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
/**
* Description of the Class
*
* @author ananth
* @version $Id$
* @created November 2, 2004
*/
public class WebdavManager {
private static Log LOG = LogFactory.getLog(WebdavManager.class);
protected static MessageDigest md5Helper = null;
private String fileLibraryPath = null;
private boolean modulesBuilt = false;
//User List cache
private HashMap users = new HashMap();
//Webdav Module cache
private WebdavModuleList moduleList = new WebdavModuleList();
private java.sql.Timestamp creationDate = new java.sql.Timestamp(System.currentTimeMillis());
/**
* Sets the fileLibraryPath attribute of the WebdavManager object
*
* @param tmp The new fileLibraryPath value
*/
public void setFileLibraryPath(String tmp) {
this.fileLibraryPath = tmp;
}
/**
* Sets the modulesBuilt attribute of the WebdavManager object
*
* @param tmp The new modulesBuilt value
*/
public void setModulesBuilt(boolean tmp) {
this.modulesBuilt = tmp;
}
/**
* Sets the modulesBuilt attribute of the WebdavManager object
*
* @param tmp The new modulesBuilt value
*/
public void setModulesBuilt(String tmp) {
this.modulesBuilt = DatabaseUtils.parseBoolean(tmp);
}
/**
* Sets the users attribute of the WebdavManager object
*
* @param tmp The new users value
*/
public void setUsers(HashMap tmp) {
this.users = tmp;
}
/**
* Sets the moduleList attribute of the WebdavManager object
*
* @param tmp The new moduleList value
*/
public void setModuleList(WebdavModuleList tmp) {
this.moduleList = tmp;
}
/**
* Gets the fileLibraryPath attribute of the WebdavManager object
*
* @return The fileLibraryPath value
*/
public String getFileLibraryPath() {
return fileLibraryPath;
}
/**
* Gets the modulesBuilt attribute of the WebdavManager object
*
* @return The modulesBuilt value
*/
public boolean getModulesBuilt() {
return modulesBuilt;
}
/**
* Gets the users attribute of the WebdavManager object
*
* @return The users value
*/
public HashMap getUsers() {
return users;
}
/**
* Gets the moduleList attribute of the WebdavManager object
*
* @return The moduleList value
*/
public WebdavModuleList getModuleList() {
return moduleList;
}
/**
* Constructor for the WebdavManager object
*/
public WebdavManager() {
}
/**
* Constructor for the WebdavManager object
*
* @param fileLibraryPath Description of the Parameter
*/
public WebdavManager(String fileLibraryPath) {
this.fileLibraryPath = fileLibraryPath;
}
/**
* Description of the Method
*
* @param db Description of the Parameter
* @param fileLibraryPath Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void buildModules(Connection db, String fileLibraryPath) throws SQLException {
if (System.getProperty("DEBUG") != null) {
System.out.println("WebdavManager-> buildModules");
}
this.fileLibraryPath = fileLibraryPath;
// build the modules
moduleList.clear();
moduleList.setFileLibraryPath(fileLibraryPath);
moduleList.setBuildContext(true);
moduleList.buildList(db);
modulesBuilt = true;
}
/**
* used by basic authentication scheme
*
* @param db Description of the Parameter
* @param username Description of the Parameter
* @param password Description of the Parameter
* @return Description of the Return Value
* @throws SQLException Description of the Exception
*/
public boolean allowUser(Connection db, String username, String password) throws SQLException {
boolean status = false;
PreparedStatement pst = db.prepareStatement(
"SELECT password, expiration, user_id " +
"FROM users " +
"WHERE username = ? " +
"AND enabled = ? ");
pst.setString(1, username);
pst.setBoolean(2, true);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
//TODO: determine if the user account has not expired
String pw = rs.getString("password");
if (pw.equals(PasswordHash.encrypt(password))) {
int userId = rs.getInt("user_id");
int roleId = -1;
WebdavUser user = new WebdavUser();
user.setUserId(userId);
user.setRoleId(roleId);
users.put(username.toLowerCase(), user);
status = true;
}
}
rs.close();
pst.close();
return status;
}
/**
* Description of the Method
*
* @param db Description of the Parameter
* @param username Description of the Parameter
* @param nonce The feature to be added to the User attribute
* @return Description of the Return Value
* @throws SQLException Description of the Exception
*/
public boolean addUser(Connection db, String username, String nonce) throws SQLException {
boolean status = false;
PreparedStatement pst = db.prepareStatement(
"SELECT user_id, webdav_access, webdav_password " +
"FROM users " +
"WHERE username = ? " +
"AND webdav_access = ? " +
"AND enabled = ? ");
pst.setString(1, username);
pst.setBoolean(2, true);
pst.setBoolean(3, true);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
//TODO: determine if the user account has not expired
int userId = rs.getInt("user_id");
int roleId = -1;
String digest = rs.getString("webdav_password");
WebdavUser user = new WebdavUser();
user.setUserId(userId);
user.setRoleId(roleId);
user.setDigest(digest);
user.setNonce(nonce);
users.put(username, user);
status = true;
}
rs.close();
pst.close();
return status;
}
/**
* Gets the webdavPassword attribute of the WebdavManager object
*
* @param db Description of the Parameter
* @param username Description of the Parameter
* @return The webdavPassword value
* @throws SQLException Description of the Exception
*/
public static String getWebdavPassword(Connection db, String username) throws SQLException {
String password = "";
PreparedStatement pst = db.prepareStatement(
"SELECT webdav_password " +
"FROM users " +
"WHERE username = ? " +
"AND enabled = ? ");
pst.setString(1, username);
pst.setBoolean(2, true);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
password = rs.getString("webdav_password");
}
rs.close();
pst.close();
return password;
}
/**
* Description of the Method
*
* @param username Description of the Parameter
* @return Description of the Return Value
*/
public boolean hasUser(String username) {
return (users.containsKey(username.toLowerCase()));
}
/**
* Gets the user attribute of the WebdavManager object
*
* @param username Description of the Parameter
* @return The user value
*/
public WebdavUser getUser(String username) {
return ((WebdavUser) users.get(username.toLowerCase()));
}
/**
* Description of the Method
*
* @param username Description of the Parameter
*/
public void removeUser(String username) {
if (hasUser(username)) {
users.remove(username.toLowerCase());
}
}
/**
* Iterates through the cached top level webdav modules for this system and
* determines if the user has permission to view this module. If so the
* modules list of bindings are populated
*
* @param db Description of the Parameter
* @param username Description of the Parameter
* @return The resources value
* @throws SQLException Description of the Exception
*/
public ModuleContext getResources(Connection db, String username) throws SQLException {
WebdavUser user = this.getUser(username);
BaseWebdavContext context = new BaseWebdavContext(user.getUserId(), fileLibraryPath);
Iterator i = moduleList.keySet().iterator();
while (i.hasNext()) {
String moduleName = (String) i.next();
WebdavModule module = (WebdavModule) moduleList.get(moduleName);
String permission = module.getContext().getPermission();
if (hasPermission(user.getUserId(), permission)) {
context.getBindings().put(moduleName, module.getContext());
context.buildProperties(moduleName, module.getEntered(), module.getModified(), new Integer(0));
}
}
//TODO: Remove this hardcoding
BaseWebdavContext synchronization = new BaseWebdavContext();
//CalendarContext calendar = new CalendarContext();
//synchronization.getBindings().put("Calendars", calendar);
//synchronization.buildProperties("Calendars", creationDate, creationDate, new Integer(0));
context.getBindings().put("Synchronization", synchronization);
context.buildProperties("Synchronization", creationDate, creationDate, new Integer(0));
return context;
}
/**
* Description of the Method
*
* @param userId Description of the Parameter
* @param permission Description of the Parameter
* @return Description of the Return Value
*/
public boolean hasPermission(int userId, String permission) {
//return thisSystem.hasPermission(userId, permission);
return true;
}
public static int validateUser(Connection db, HttpServletRequest req) throws Exception {
String argHeader = req.getHeader("Authorization");
HashMap params = getAuthenticationParams(argHeader);
String username = (String) params.get("username");
if (md5Helper == null) {
md5Helper = MessageDigest.getInstance("MD5");
}
int userId = -1;
String password = null;
PreparedStatement pst = db.prepareStatement(
"SELECT user_id, webdav_password " +
"FROM users " +
"WHERE username = ? " +
"AND enabled = ? ");
pst.setString(1, username);
pst.setBoolean(2, true);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
userId = rs.getInt("user_id");
password = rs.getString("webdav_password");
}
rs.close();
pst.close();
if (userId == -1) {
return userId;
}
String method = req.getMethod();
String uri = (String) params.get("uri");
String a2 = MD5Encoder.encode(md5Helper.digest((method + ":" + uri).getBytes()));
String digest =
MD5Encoder.encode(
md5Helper.digest(
(password + ":" +
params.get("nonce") + ":" +
a2).getBytes()));
if (!digest.equals(params.get("response"))) {
userId = -1;
}
return userId;
}
protected static HashMap getAuthenticationParams(String argHeader) {
HashMap params = new HashMap();
StringTokenizer st = new StringTokenizer(argHeader, ",");
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (token.startsWith("Digest")) {
token = token.substring("Digest".length());
}
if (token.contains("=") && token.contains("\"")) {
String param = token.substring(0, token.indexOf("=")).trim();
String value = token.substring(token.indexOf("\"") + 1, token.lastIndexOf("\""));
params.put(param, value);
}
}
return params;
}
public static boolean checkAuthentication(HttpServletRequest req) throws Exception {
String argHeader = req.getHeader("Authorization");
if (argHeader == null || !argHeader.startsWith("Digest")) {
LOG.debug("Missing Digest in header");
return false;
}
return true;
}
public static void askForAuthentication(HttpServletResponse res) throws Exception {
String nonce = DefaultServlet.generateNonce();
// determine the 'opaque' value which should be returned as-is by the client
String opaque = DefaultServlet.generateOpaque();
res.setHeader(
"WWW-Authenticate", "Digest realm=\"" + WebdavServlet.USER_REALM + "\", " +
"nonce=\"" + nonce + "\", " +
"opaque=\"" + opaque + "\"");
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}